diff options
| author | 2020-03-14 09:48:15 -0400 | |
|---|---|---|
| committer | 2020-03-14 09:48:15 -0400 | |
| commit | 35145bd529c3517e2c366efc764a762092d96edf (patch) | |
| tree | 58c80a2133092b990ca11f3a357d70fab2c5fd0b /src | |
| parent | Merge pull request #3473 from ReinUsesLisp/shader-purge (diff) | |
| parent | vk/gl_shader_decompiler: Silence assertion on compute (diff) | |
| download | yuzu-35145bd529c3517e2c366efc764a762092d96edf.tar.gz yuzu-35145bd529c3517e2c366efc764a762092d96edf.tar.xz yuzu-35145bd529c3517e2c366efc764a762092d96edf.zip | |
Merge pull request #3490 from ReinUsesLisp/transform-feedbacks
video_core: Initial implementation of transform feedbacks
Diffstat (limited to 'src')
| -rw-r--r-- | src/common/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/video_core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/video_core/engines/maxwell_3d.h | 15 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.cpp | 70 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.h | 14 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_decompiler.cpp | 138 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_device.cpp | 47 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_device.h | 45 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | 17 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_pipeline_cache.h | 4 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_rasterizer.cpp | 42 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_rasterizer.h | 4 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_shader_decompiler.cpp | 138 | ||||
| -rw-r--r-- | src/video_core/renderer_vulkan/vk_shader_decompiler.h | 13 | ||||
| -rw-r--r-- | src/video_core/shader/transform_feedback.cpp | 115 | ||||
| -rw-r--r-- | src/video_core/shader/transform_feedback.h | 23 |
16 files changed, 574 insertions, 115 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 1f621fb1f..fbebed715 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -83,6 +83,8 @@ add_custom_command(OUTPUT scm_rev.cpp | |||
| 83 | "${VIDEO_CORE}/shader/shader_ir.cpp" | 83 | "${VIDEO_CORE}/shader/shader_ir.cpp" |
| 84 | "${VIDEO_CORE}/shader/shader_ir.h" | 84 | "${VIDEO_CORE}/shader/shader_ir.h" |
| 85 | "${VIDEO_CORE}/shader/track.cpp" | 85 | "${VIDEO_CORE}/shader/track.cpp" |
| 86 | "${VIDEO_CORE}/shader/transform_feedback.cpp" | ||
| 87 | "${VIDEO_CORE}/shader/transform_feedback.h" | ||
| 86 | # and also check that the scm_rev files haven't changed | 88 | # and also check that the scm_rev files haven't changed |
| 87 | "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" | 89 | "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" |
| 88 | "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.h" | 90 | "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.h" |
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 0101e5f0e..91df062d7 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -129,6 +129,8 @@ add_library(video_core STATIC | |||
| 129 | shader/shader_ir.cpp | 129 | shader/shader_ir.cpp |
| 130 | shader/shader_ir.h | 130 | shader/shader_ir.h |
| 131 | shader/track.cpp | 131 | shader/track.cpp |
| 132 | shader/transform_feedback.cpp | ||
| 133 | shader/transform_feedback.h | ||
| 132 | surface.cpp | 134 | surface.cpp |
| 133 | surface.h | 135 | surface.h |
| 134 | texture_cache/format_lookup_table.cpp | 136 | texture_cache/format_lookup_table.cpp |
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 8752a1cfb..8a9e9992e 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -628,19 +628,26 @@ public: | |||
| 628 | float depth_range_far; | 628 | float depth_range_far; |
| 629 | }; | 629 | }; |
| 630 | 630 | ||
| 631 | struct alignas(32) TransformFeedbackBinding { | 631 | struct TransformFeedbackBinding { |
| 632 | u32 buffer_enable; | 632 | u32 buffer_enable; |
| 633 | u32 address_high; | 633 | u32 address_high; |
| 634 | u32 address_low; | 634 | u32 address_low; |
| 635 | s32 buffer_size; | 635 | s32 buffer_size; |
| 636 | s32 buffer_offset; | 636 | s32 buffer_offset; |
| 637 | INSERT_UNION_PADDING_WORDS(3); | ||
| 638 | |||
| 639 | GPUVAddr Address() const { | ||
| 640 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | | ||
| 641 | address_low); | ||
| 642 | } | ||
| 637 | }; | 643 | }; |
| 638 | static_assert(sizeof(TransformFeedbackBinding) == 32); | 644 | static_assert(sizeof(TransformFeedbackBinding) == 32); |
| 639 | 645 | ||
| 640 | struct alignas(16) TransformFeedbackLayout { | 646 | struct TransformFeedbackLayout { |
| 641 | u32 stream; | 647 | u32 stream; |
| 642 | u32 varying_count; | 648 | u32 varying_count; |
| 643 | u32 stride; | 649 | u32 stride; |
| 650 | INSERT_UNION_PADDING_WORDS(1); | ||
| 644 | }; | 651 | }; |
| 645 | static_assert(sizeof(TransformFeedbackLayout) == 16); | 652 | static_assert(sizeof(TransformFeedbackLayout) == 16); |
| 646 | 653 | ||
| @@ -652,6 +659,10 @@ public: | |||
| 652 | return shader_config[index].enable != 0; | 659 | return shader_config[index].enable != 0; |
| 653 | } | 660 | } |
| 654 | 661 | ||
| 662 | bool IsShaderConfigEnabled(Regs::ShaderProgram type) const { | ||
| 663 | return IsShaderConfigEnabled(static_cast<std::size_t>(type)); | ||
| 664 | } | ||
| 665 | |||
| 655 | union { | 666 | union { |
| 656 | struct { | 667 | struct { |
| 657 | INSERT_UNION_PADDING_WORDS(0x45); | 668 | INSERT_UNION_PADDING_WORDS(0x45); |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 8a2db8e36..1af4268a4 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -496,7 +496,6 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { | |||
| 496 | SyncCullMode(); | 496 | SyncCullMode(); |
| 497 | SyncPrimitiveRestart(); | 497 | SyncPrimitiveRestart(); |
| 498 | SyncScissorTest(); | 498 | SyncScissorTest(); |
| 499 | SyncTransformFeedback(); | ||
| 500 | SyncPointState(); | 499 | SyncPointState(); |
| 501 | SyncPolygonOffset(); | 500 | SyncPolygonOffset(); |
| 502 | SyncAlphaTest(); | 501 | SyncAlphaTest(); |
| @@ -569,7 +568,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { | |||
| 569 | glTextureBarrier(); | 568 | glTextureBarrier(); |
| 570 | } | 569 | } |
| 571 | 570 | ||
| 572 | ++num_queued_commands; | 571 | BeginTransformFeedback(primitive_mode); |
| 573 | 572 | ||
| 574 | const GLuint base_instance = static_cast<GLuint>(gpu.regs.vb_base_instance); | 573 | const GLuint base_instance = static_cast<GLuint>(gpu.regs.vb_base_instance); |
| 575 | const GLsizei num_instances = | 574 | const GLsizei num_instances = |
| @@ -608,6 +607,10 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) { | |||
| 608 | num_instances, base_instance); | 607 | num_instances, base_instance); |
| 609 | } | 608 | } |
| 610 | } | 609 | } |
| 610 | |||
| 611 | EndTransformFeedback(); | ||
| 612 | |||
| 613 | ++num_queued_commands; | ||
| 611 | } | 614 | } |
| 612 | 615 | ||
| 613 | void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) { | 616 | void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) { |
| @@ -1290,11 +1293,6 @@ void RasterizerOpenGL::SyncScissorTest() { | |||
| 1290 | } | 1293 | } |
| 1291 | } | 1294 | } |
| 1292 | 1295 | ||
| 1293 | void RasterizerOpenGL::SyncTransformFeedback() { | ||
| 1294 | const auto& regs = system.GPU().Maxwell3D().regs; | ||
| 1295 | UNIMPLEMENTED_IF_MSG(regs.tfb_enabled != 0, "Transform feedbacks are not implemented"); | ||
| 1296 | } | ||
| 1297 | |||
| 1298 | void RasterizerOpenGL::SyncPointState() { | 1296 | void RasterizerOpenGL::SyncPointState() { |
| 1299 | auto& gpu = system.GPU().Maxwell3D(); | 1297 | auto& gpu = system.GPU().Maxwell3D(); |
| 1300 | auto& flags = gpu.dirty.flags; | 1298 | auto& flags = gpu.dirty.flags; |
| @@ -1370,4 +1368,62 @@ void RasterizerOpenGL::SyncFramebufferSRGB() { | |||
| 1370 | oglEnable(GL_FRAMEBUFFER_SRGB, gpu.regs.framebuffer_srgb); | 1368 | oglEnable(GL_FRAMEBUFFER_SRGB, gpu.regs.framebuffer_srgb); |
| 1371 | } | 1369 | } |
| 1372 | 1370 | ||
| 1371 | void RasterizerOpenGL::BeginTransformFeedback(GLenum primitive_mode) { | ||
| 1372 | const auto& regs = system.GPU().Maxwell3D().regs; | ||
| 1373 | if (regs.tfb_enabled == 0) { | ||
| 1374 | return; | ||
| 1375 | } | ||
| 1376 | |||
| 1377 | UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) || | ||
| 1378 | regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) || | ||
| 1379 | regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::Geometry)); | ||
| 1380 | |||
| 1381 | for (std::size_t index = 0; index < Maxwell::NumTransformFeedbackBuffers; ++index) { | ||
| 1382 | const auto& binding = regs.tfb_bindings[index]; | ||
| 1383 | if (!binding.buffer_enable) { | ||
| 1384 | if (enabled_transform_feedback_buffers[index]) { | ||
| 1385 | glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, static_cast<GLuint>(index), 0, 0, | ||
| 1386 | 0); | ||
| 1387 | } | ||
| 1388 | enabled_transform_feedback_buffers[index] = false; | ||
| 1389 | continue; | ||
| 1390 | } | ||
| 1391 | enabled_transform_feedback_buffers[index] = true; | ||
| 1392 | |||
| 1393 | auto& tfb_buffer = transform_feedback_buffers[index]; | ||
| 1394 | tfb_buffer.Create(); | ||
| 1395 | |||
| 1396 | const GLuint handle = tfb_buffer.handle; | ||
| 1397 | const std::size_t size = binding.buffer_size; | ||
| 1398 | glNamedBufferData(handle, static_cast<GLsizeiptr>(size), nullptr, GL_STREAM_COPY); | ||
| 1399 | glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, static_cast<GLuint>(index), handle, 0, | ||
| 1400 | static_cast<GLsizeiptr>(size)); | ||
| 1401 | } | ||
| 1402 | |||
| 1403 | glBeginTransformFeedback(GL_POINTS); | ||
| 1404 | } | ||
| 1405 | |||
| 1406 | void RasterizerOpenGL::EndTransformFeedback() { | ||
| 1407 | const auto& regs = system.GPU().Maxwell3D().regs; | ||
| 1408 | if (regs.tfb_enabled == 0) { | ||
| 1409 | return; | ||
| 1410 | } | ||
| 1411 | |||
| 1412 | glEndTransformFeedback(); | ||
| 1413 | |||
| 1414 | for (std::size_t index = 0; index < Maxwell::NumTransformFeedbackBuffers; ++index) { | ||
| 1415 | const auto& binding = regs.tfb_bindings[index]; | ||
| 1416 | if (!binding.buffer_enable) { | ||
| 1417 | continue; | ||
| 1418 | } | ||
| 1419 | UNIMPLEMENTED_IF(binding.buffer_offset != 0); | ||
| 1420 | |||
| 1421 | const GLuint handle = transform_feedback_buffers[index].handle; | ||
| 1422 | const GPUVAddr gpu_addr = binding.Address(); | ||
| 1423 | const std::size_t size = binding.buffer_size; | ||
| 1424 | const auto [dest_buffer, offset] = buffer_cache.UploadMemory(gpu_addr, size, 4, true); | ||
| 1425 | glCopyNamedBufferSubData(handle, *dest_buffer, 0, offset, static_cast<GLsizeiptr>(size)); | ||
| 1426 | } | ||
| 1427 | } | ||
| 1428 | |||
| 1373 | } // namespace OpenGL | 1429 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index e6424f5d2..2d3be2437 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -168,9 +168,6 @@ private: | |||
| 168 | /// Syncs the scissor test state to match the guest state | 168 | /// Syncs the scissor test state to match the guest state |
| 169 | void SyncScissorTest(); | 169 | void SyncScissorTest(); |
| 170 | 170 | ||
| 171 | /// Syncs the transform feedback state to match the guest state | ||
| 172 | void SyncTransformFeedback(); | ||
| 173 | |||
| 174 | /// Syncs the point state to match the guest state | 171 | /// Syncs the point state to match the guest state |
| 175 | void SyncPointState(); | 172 | void SyncPointState(); |
| 176 | 173 | ||
| @@ -192,6 +189,12 @@ private: | |||
| 192 | /// Syncs the framebuffer sRGB state to match the guest state | 189 | /// Syncs the framebuffer sRGB state to match the guest state |
| 193 | void SyncFramebufferSRGB(); | 190 | void SyncFramebufferSRGB(); |
| 194 | 191 | ||
| 192 | /// Begin a transform feedback | ||
| 193 | void BeginTransformFeedback(GLenum primitive_mode); | ||
| 194 | |||
| 195 | /// End a transform feedback | ||
| 196 | void EndTransformFeedback(); | ||
| 197 | |||
| 195 | /// Check for extension that are not strictly required but are needed for correct emulation | 198 | /// Check for extension that are not strictly required but are needed for correct emulation |
| 196 | void CheckExtensions(); | 199 | void CheckExtensions(); |
| 197 | 200 | ||
| @@ -229,6 +232,11 @@ private: | |||
| 229 | BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER}; | 232 | BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER}; |
| 230 | BindBuffersRangePushBuffer bind_ssbo_pushbuffer{GL_SHADER_STORAGE_BUFFER}; | 233 | BindBuffersRangePushBuffer bind_ssbo_pushbuffer{GL_SHADER_STORAGE_BUFFER}; |
| 231 | 234 | ||
| 235 | std::array<OGLBuffer, Tegra::Engines::Maxwell3D::Regs::NumTransformFeedbackBuffers> | ||
| 236 | transform_feedback_buffers; | ||
| 237 | std::bitset<Tegra::Engines::Maxwell3D::Regs::NumTransformFeedbackBuffers> | ||
| 238 | enabled_transform_feedback_buffers; | ||
| 239 | |||
| 232 | /// Number of commands queued to the OpenGL driver. Reseted on flush. | 240 | /// Number of commands queued to the OpenGL driver. Reseted on flush. |
| 233 | std::size_t num_queued_commands = 0; | 241 | std::size_t num_queued_commands = 0; |
| 234 | 242 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 19d6f3dcb..3adf7f0cb 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #include "video_core/shader/ast.h" | 23 | #include "video_core/shader/ast.h" |
| 24 | #include "video_core/shader/node.h" | 24 | #include "video_core/shader/node.h" |
| 25 | #include "video_core/shader/shader_ir.h" | 25 | #include "video_core/shader/shader_ir.h" |
| 26 | #include "video_core/shader/transform_feedback.h" | ||
| 26 | 27 | ||
| 27 | namespace OpenGL { | 28 | namespace OpenGL { |
| 28 | 29 | ||
| @@ -36,6 +37,7 @@ using Tegra::Shader::IpaInterpMode; | |||
| 36 | using Tegra::Shader::IpaMode; | 37 | using Tegra::Shader::IpaMode; |
| 37 | using Tegra::Shader::IpaSampleMode; | 38 | using Tegra::Shader::IpaSampleMode; |
| 38 | using Tegra::Shader::Register; | 39 | using Tegra::Shader::Register; |
| 40 | using VideoCommon::Shader::BuildTransformFeedback; | ||
| 39 | using VideoCommon::Shader::Registry; | 41 | using VideoCommon::Shader::Registry; |
| 40 | 42 | ||
| 41 | using namespace std::string_literals; | 43 | using namespace std::string_literals; |
| @@ -49,6 +51,11 @@ class ExprDecompiler; | |||
| 49 | 51 | ||
| 50 | enum class Type { Void, Bool, Bool2, Float, Int, Uint, HalfFloat }; | 52 | enum class Type { Void, Bool, Bool2, Float, Int, Uint, HalfFloat }; |
| 51 | 53 | ||
| 54 | constexpr std::array FLOAT_TYPES{"float", "vec2", "vec3", "vec4"}; | ||
| 55 | |||
| 56 | constexpr std::string_view INPUT_ATTRIBUTE_NAME = "in_attr"; | ||
| 57 | constexpr std::string_view OUTPUT_ATTRIBUTE_NAME = "out_attr"; | ||
| 58 | |||
| 52 | struct TextureOffset {}; | 59 | struct TextureOffset {}; |
| 53 | struct TextureDerivates {}; | 60 | struct TextureDerivates {}; |
| 54 | using TextureArgument = std::pair<Type, Node>; | 61 | using TextureArgument = std::pair<Type, Node>; |
| @@ -390,12 +397,22 @@ std::string FlowStackTopName(MetaStackClass stack) { | |||
| 390 | return stage == ShaderType::Vertex; | 397 | return stage == ShaderType::Vertex; |
| 391 | } | 398 | } |
| 392 | 399 | ||
| 400 | struct GenericVaryingDescription { | ||
| 401 | std::string name; | ||
| 402 | u8 first_element = 0; | ||
| 403 | bool is_scalar = false; | ||
| 404 | }; | ||
| 405 | |||
| 393 | class GLSLDecompiler final { | 406 | class GLSLDecompiler final { |
| 394 | public: | 407 | public: |
| 395 | explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, const Registry& registry, | 408 | explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, const Registry& registry, |
| 396 | ShaderType stage, std::string_view identifier, std::string_view suffix) | 409 | ShaderType stage, std::string_view identifier, std::string_view suffix) |
| 397 | : device{device}, ir{ir}, registry{registry}, stage{stage}, | 410 | : device{device}, ir{ir}, registry{registry}, stage{stage}, |
| 398 | identifier{identifier}, suffix{suffix}, header{ir.GetHeader()} {} | 411 | identifier{identifier}, suffix{suffix}, header{ir.GetHeader()} { |
| 412 | if (stage != ShaderType::Compute) { | ||
| 413 | transform_feedback = BuildTransformFeedback(registry.GetGraphicsInfo()); | ||
| 414 | } | ||
| 415 | } | ||
| 399 | 416 | ||
| 400 | void Decompile() { | 417 | void Decompile() { |
| 401 | DeclareHeader(); | 418 | DeclareHeader(); |
| @@ -403,17 +420,17 @@ public: | |||
| 403 | DeclareGeometry(); | 420 | DeclareGeometry(); |
| 404 | DeclareFragment(); | 421 | DeclareFragment(); |
| 405 | DeclareCompute(); | 422 | DeclareCompute(); |
| 406 | DeclareRegisters(); | ||
| 407 | DeclareCustomVariables(); | ||
| 408 | DeclarePredicates(); | ||
| 409 | DeclareLocalMemory(); | ||
| 410 | DeclareInternalFlags(); | ||
| 411 | DeclareInputAttributes(); | 423 | DeclareInputAttributes(); |
| 412 | DeclareOutputAttributes(); | 424 | DeclareOutputAttributes(); |
| 413 | DeclareConstantBuffers(); | ||
| 414 | DeclareGlobalMemory(); | ||
| 415 | DeclareSamplers(); | ||
| 416 | DeclareImages(); | 425 | DeclareImages(); |
| 426 | DeclareSamplers(); | ||
| 427 | DeclareGlobalMemory(); | ||
| 428 | DeclareConstantBuffers(); | ||
| 429 | DeclareLocalMemory(); | ||
| 430 | DeclareRegisters(); | ||
| 431 | DeclarePredicates(); | ||
| 432 | DeclareInternalFlags(); | ||
| 433 | DeclareCustomVariables(); | ||
| 417 | DeclarePhysicalAttributeReader(); | 434 | DeclarePhysicalAttributeReader(); |
| 418 | 435 | ||
| 419 | code.AddLine("void main() {{"); | 436 | code.AddLine("void main() {{"); |
| @@ -485,7 +502,7 @@ private: | |||
| 485 | if (!identifier.empty()) { | 502 | if (!identifier.empty()) { |
| 486 | code.AddLine("// {}", identifier); | 503 | code.AddLine("// {}", identifier); |
| 487 | } | 504 | } |
| 488 | code.AddLine("#version 430 core"); | 505 | code.AddLine("#version 440 core"); |
| 489 | code.AddLine("#extension GL_ARB_separate_shader_objects : enable"); | 506 | code.AddLine("#extension GL_ARB_separate_shader_objects : enable"); |
| 490 | if (device.HasShaderBallot()) { | 507 | if (device.HasShaderBallot()) { |
| 491 | code.AddLine("#extension GL_ARB_shader_ballot : require"); | 508 | code.AddLine("#extension GL_ARB_shader_ballot : require"); |
| @@ -570,7 +587,13 @@ private: | |||
| 570 | code.AddLine("out gl_PerVertex {{"); | 587 | code.AddLine("out gl_PerVertex {{"); |
| 571 | ++code.scope; | 588 | ++code.scope; |
| 572 | 589 | ||
| 573 | code.AddLine("vec4 gl_Position;"); | 590 | auto pos_xfb = GetTransformFeedbackDecoration(Attribute::Index::Position); |
| 591 | if (!pos_xfb.empty()) { | ||
| 592 | pos_xfb = fmt::format("layout ({}) ", pos_xfb); | ||
| 593 | } | ||
| 594 | const char* pos_type = | ||
| 595 | FLOAT_TYPES.at(GetNumComponents(Attribute::Index::Position).value_or(4) - 1); | ||
| 596 | code.AddLine("{}{} gl_Position;", pos_xfb, pos_type); | ||
| 574 | 597 | ||
| 575 | for (const auto attribute : ir.GetOutputAttributes()) { | 598 | for (const auto attribute : ir.GetOutputAttributes()) { |
| 576 | if (attribute == Attribute::Index::ClipDistances0123 || | 599 | if (attribute == Attribute::Index::ClipDistances0123 || |
| @@ -703,7 +726,7 @@ private: | |||
| 703 | void DeclareInputAttribute(Attribute::Index index, bool skip_unused) { | 726 | void DeclareInputAttribute(Attribute::Index index, bool skip_unused) { |
| 704 | const u32 location{GetGenericAttributeIndex(index)}; | 727 | const u32 location{GetGenericAttributeIndex(index)}; |
| 705 | 728 | ||
| 706 | std::string name{GetInputAttribute(index)}; | 729 | std::string name{GetGenericInputAttribute(index)}; |
| 707 | if (stage == ShaderType::Geometry) { | 730 | if (stage == ShaderType::Geometry) { |
| 708 | name = "gs_" + name + "[]"; | 731 | name = "gs_" + name + "[]"; |
| 709 | } | 732 | } |
| @@ -740,9 +763,59 @@ private: | |||
| 740 | } | 763 | } |
| 741 | } | 764 | } |
| 742 | 765 | ||
| 766 | std::optional<std::size_t> GetNumComponents(Attribute::Index index, u8 element = 0) const { | ||
| 767 | const u8 location = static_cast<u8>(static_cast<u32>(index) * 4 + element); | ||
| 768 | const auto it = transform_feedback.find(location); | ||
| 769 | if (it == transform_feedback.end()) { | ||
| 770 | return {}; | ||
| 771 | } | ||
| 772 | return it->second.components; | ||
| 773 | } | ||
| 774 | |||
| 775 | std::string GetTransformFeedbackDecoration(Attribute::Index index, u8 element = 0) const { | ||
| 776 | const u8 location = static_cast<u8>(static_cast<u32>(index) * 4 + element); | ||
| 777 | const auto it = transform_feedback.find(location); | ||
| 778 | if (it == transform_feedback.end()) { | ||
| 779 | return {}; | ||
| 780 | } | ||
| 781 | |||
| 782 | const VaryingTFB& tfb = it->second; | ||
| 783 | return fmt::format("xfb_buffer = {}, xfb_offset = {}, xfb_stride = {}", tfb.buffer, | ||
| 784 | tfb.offset, tfb.stride); | ||
| 785 | } | ||
| 786 | |||
| 743 | void DeclareOutputAttribute(Attribute::Index index) { | 787 | void DeclareOutputAttribute(Attribute::Index index) { |
| 744 | const u32 location{GetGenericAttributeIndex(index)}; | 788 | static constexpr std::string_view swizzle = "xyzw"; |
| 745 | code.AddLine("layout (location = {}) out vec4 {};", location, GetOutputAttribute(index)); | 789 | u8 element = 0; |
| 790 | while (element < 4) { | ||
| 791 | auto xfb = GetTransformFeedbackDecoration(index, element); | ||
| 792 | if (!xfb.empty()) { | ||
| 793 | xfb = fmt::format(", {}", xfb); | ||
| 794 | } | ||
| 795 | const std::size_t remainder = 4 - element; | ||
| 796 | const std::size_t num_components = GetNumComponents(index, element).value_or(remainder); | ||
| 797 | const char* const type = FLOAT_TYPES.at(num_components - 1); | ||
| 798 | |||
| 799 | const u32 location = GetGenericAttributeIndex(index); | ||
| 800 | |||
| 801 | GenericVaryingDescription description; | ||
| 802 | description.first_element = static_cast<u8>(element); | ||
| 803 | description.is_scalar = num_components == 1; | ||
| 804 | description.name = AppendSuffix(location, OUTPUT_ATTRIBUTE_NAME); | ||
| 805 | if (element != 0 || num_components != 4) { | ||
| 806 | const std::string_view name_swizzle = swizzle.substr(element, num_components); | ||
| 807 | description.name = fmt::format("{}_{}", description.name, name_swizzle); | ||
| 808 | } | ||
| 809 | for (std::size_t i = 0; i < num_components; ++i) { | ||
| 810 | const u8 offset = static_cast<u8>(location * 4 + element + i); | ||
| 811 | varying_description.insert({offset, description}); | ||
| 812 | } | ||
| 813 | |||
| 814 | code.AddLine("layout (location = {}, component = {}{}) out {} {};", location, element, | ||
| 815 | xfb, type, description.name); | ||
| 816 | |||
| 817 | element = static_cast<u8>(static_cast<std::size_t>(element) + num_components); | ||
| 818 | } | ||
| 746 | } | 819 | } |
| 747 | 820 | ||
| 748 | void DeclareConstantBuffers() { | 821 | void DeclareConstantBuffers() { |
| @@ -1095,7 +1168,7 @@ private: | |||
| 1095 | return {"0", Type::Int}; | 1168 | return {"0", Type::Int}; |
| 1096 | default: | 1169 | default: |
| 1097 | if (IsGenericAttribute(attribute)) { | 1170 | if (IsGenericAttribute(attribute)) { |
| 1098 | return {GeometryPass(GetInputAttribute(attribute)) + GetSwizzle(element), | 1171 | return {GeometryPass(GetGenericInputAttribute(attribute)) + GetSwizzle(element), |
| 1099 | Type::Float}; | 1172 | Type::Float}; |
| 1100 | } | 1173 | } |
| 1101 | break; | 1174 | break; |
| @@ -1164,8 +1237,7 @@ private: | |||
| 1164 | return {{fmt::format("gl_ClipDistance[{}]", abuf->GetElement() + 4), Type::Float}}; | 1237 | return {{fmt::format("gl_ClipDistance[{}]", abuf->GetElement() + 4), Type::Float}}; |
| 1165 | default: | 1238 | default: |
| 1166 | if (IsGenericAttribute(attribute)) { | 1239 | if (IsGenericAttribute(attribute)) { |
| 1167 | return { | 1240 | return {{GetGenericOutputAttribute(attribute, abuf->GetElement()), Type::Float}}; |
| 1168 | {GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement()), Type::Float}}; | ||
| 1169 | } | 1241 | } |
| 1170 | UNIMPLEMENTED_MSG("Unhandled output attribute: {}", static_cast<u32>(attribute)); | 1242 | UNIMPLEMENTED_MSG("Unhandled output attribute: {}", static_cast<u32>(attribute)); |
| 1171 | return {}; | 1243 | return {}; |
| @@ -2376,27 +2448,34 @@ private: | |||
| 2376 | static_assert(operation_decompilers.size() == static_cast<std::size_t>(OperationCode::Amount)); | 2448 | static_assert(operation_decompilers.size() == static_cast<std::size_t>(OperationCode::Amount)); |
| 2377 | 2449 | ||
| 2378 | std::string GetRegister(u32 index) const { | 2450 | std::string GetRegister(u32 index) const { |
| 2379 | return GetDeclarationWithSuffix(index, "gpr"); | 2451 | return AppendSuffix(index, "gpr"); |
| 2380 | } | 2452 | } |
| 2381 | 2453 | ||
| 2382 | std::string GetCustomVariable(u32 index) const { | 2454 | std::string GetCustomVariable(u32 index) const { |
| 2383 | return GetDeclarationWithSuffix(index, "custom_var"); | 2455 | return AppendSuffix(index, "custom_var"); |
| 2384 | } | 2456 | } |
| 2385 | 2457 | ||
| 2386 | std::string GetPredicate(Tegra::Shader::Pred pred) const { | 2458 | std::string GetPredicate(Tegra::Shader::Pred pred) const { |
| 2387 | return GetDeclarationWithSuffix(static_cast<u32>(pred), "pred"); | 2459 | return AppendSuffix(static_cast<u32>(pred), "pred"); |
| 2388 | } | 2460 | } |
| 2389 | 2461 | ||
| 2390 | std::string GetInputAttribute(Attribute::Index attribute) const { | 2462 | std::string GetGenericInputAttribute(Attribute::Index attribute) const { |
| 2391 | return GetDeclarationWithSuffix(GetGenericAttributeIndex(attribute), "input_attr"); | 2463 | return AppendSuffix(GetGenericAttributeIndex(attribute), INPUT_ATTRIBUTE_NAME); |
| 2392 | } | 2464 | } |
| 2393 | 2465 | ||
| 2394 | std::string GetOutputAttribute(Attribute::Index attribute) const { | 2466 | std::unordered_map<u8, GenericVaryingDescription> varying_description; |
| 2395 | return GetDeclarationWithSuffix(GetGenericAttributeIndex(attribute), "output_attr"); | 2467 | |
| 2468 | std::string GetGenericOutputAttribute(Attribute::Index attribute, std::size_t element) const { | ||
| 2469 | const u8 offset = static_cast<u8>(GetGenericAttributeIndex(attribute) * 4 + element); | ||
| 2470 | const auto& description = varying_description.at(offset); | ||
| 2471 | if (description.is_scalar) { | ||
| 2472 | return description.name; | ||
| 2473 | } | ||
| 2474 | return fmt::format("{}[{}]", description.name, element - description.first_element); | ||
| 2396 | } | 2475 | } |
| 2397 | 2476 | ||
| 2398 | std::string GetConstBuffer(u32 index) const { | 2477 | std::string GetConstBuffer(u32 index) const { |
| 2399 | return GetDeclarationWithSuffix(index, "cbuf"); | 2478 | return AppendSuffix(index, "cbuf"); |
| 2400 | } | 2479 | } |
| 2401 | 2480 | ||
| 2402 | std::string GetGlobalMemory(const GlobalMemoryBase& descriptor) const { | 2481 | std::string GetGlobalMemory(const GlobalMemoryBase& descriptor) const { |
| @@ -2409,7 +2488,7 @@ private: | |||
| 2409 | } | 2488 | } |
| 2410 | 2489 | ||
| 2411 | std::string GetConstBufferBlock(u32 index) const { | 2490 | std::string GetConstBufferBlock(u32 index) const { |
| 2412 | return GetDeclarationWithSuffix(index, "cbuf_block"); | 2491 | return AppendSuffix(index, "cbuf_block"); |
| 2413 | } | 2492 | } |
| 2414 | 2493 | ||
| 2415 | std::string GetLocalMemory() const { | 2494 | std::string GetLocalMemory() const { |
| @@ -2434,14 +2513,14 @@ private: | |||
| 2434 | } | 2513 | } |
| 2435 | 2514 | ||
| 2436 | std::string GetSampler(const Sampler& sampler) const { | 2515 | std::string GetSampler(const Sampler& sampler) const { |
| 2437 | return GetDeclarationWithSuffix(static_cast<u32>(sampler.GetIndex()), "sampler"); | 2516 | return AppendSuffix(static_cast<u32>(sampler.GetIndex()), "sampler"); |
| 2438 | } | 2517 | } |
| 2439 | 2518 | ||
| 2440 | std::string GetImage(const Image& image) const { | 2519 | std::string GetImage(const Image& image) const { |
| 2441 | return GetDeclarationWithSuffix(static_cast<u32>(image.GetIndex()), "image"); | 2520 | return AppendSuffix(static_cast<u32>(image.GetIndex()), "image"); |
| 2442 | } | 2521 | } |
| 2443 | 2522 | ||
| 2444 | std::string GetDeclarationWithSuffix(u32 index, std::string_view name) const { | 2523 | std::string AppendSuffix(u32 index, std::string_view name) const { |
| 2445 | if (suffix.empty()) { | 2524 | if (suffix.empty()) { |
| 2446 | return fmt::format("{}{}", name, index); | 2525 | return fmt::format("{}{}", name, index); |
| 2447 | } else { | 2526 | } else { |
| @@ -2477,6 +2556,7 @@ private: | |||
| 2477 | const std::string_view identifier; | 2556 | const std::string_view identifier; |
| 2478 | const std::string_view suffix; | 2557 | const std::string_view suffix; |
| 2479 | const Header header; | 2558 | const Header header; |
| 2559 | std::unordered_map<u8, VaryingTFB> transform_feedback; | ||
| 2480 | 2560 | ||
| 2481 | ShaderWriter code; | 2561 | ShaderWriter code; |
| 2482 | 2562 | ||
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp index 886bde3b9..3847bd722 100644 --- a/src/video_core/renderer_vulkan/vk_device.cpp +++ b/src/video_core/renderer_vulkan/vk_device.cpp | |||
| @@ -107,8 +107,7 @@ bool VKDevice::Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instan | |||
| 107 | features.occlusionQueryPrecise = true; | 107 | features.occlusionQueryPrecise = true; |
| 108 | features.fragmentStoresAndAtomics = true; | 108 | features.fragmentStoresAndAtomics = true; |
| 109 | features.shaderImageGatherExtended = true; | 109 | features.shaderImageGatherExtended = true; |
| 110 | features.shaderStorageImageReadWithoutFormat = | 110 | features.shaderStorageImageReadWithoutFormat = is_formatless_image_load_supported; |
| 111 | is_shader_storage_img_read_without_format_supported; | ||
| 112 | features.shaderStorageImageWriteWithoutFormat = true; | 111 | features.shaderStorageImageWriteWithoutFormat = true; |
| 113 | features.textureCompressionASTC_LDR = is_optimal_astc_supported; | 112 | features.textureCompressionASTC_LDR = is_optimal_astc_supported; |
| 114 | 113 | ||
| @@ -148,6 +147,15 @@ bool VKDevice::Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instan | |||
| 148 | LOG_INFO(Render_Vulkan, "Device doesn't support uint8 indexes"); | 147 | LOG_INFO(Render_Vulkan, "Device doesn't support uint8 indexes"); |
| 149 | } | 148 | } |
| 150 | 149 | ||
| 150 | vk::PhysicalDeviceTransformFeedbackFeaturesEXT transform_feedback; | ||
| 151 | if (ext_transform_feedback) { | ||
| 152 | transform_feedback.transformFeedback = true; | ||
| 153 | transform_feedback.geometryStreams = true; | ||
| 154 | SetNext(next, transform_feedback); | ||
| 155 | } else { | ||
| 156 | LOG_INFO(Render_Vulkan, "Device doesn't support transform feedbacks"); | ||
| 157 | } | ||
| 158 | |||
| 151 | if (!ext_depth_range_unrestricted) { | 159 | if (!ext_depth_range_unrestricted) { |
| 152 | LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted"); | 160 | LOG_INFO(Render_Vulkan, "Device doesn't support depth range unrestricted"); |
| 153 | } | 161 | } |
| @@ -385,7 +393,7 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami | |||
| 385 | } | 393 | } |
| 386 | }; | 394 | }; |
| 387 | 395 | ||
| 388 | extensions.reserve(14); | 396 | extensions.reserve(15); |
| 389 | extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); | 397 | extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME); |
| 390 | extensions.push_back(VK_KHR_16BIT_STORAGE_EXTENSION_NAME); | 398 | extensions.push_back(VK_KHR_16BIT_STORAGE_EXTENSION_NAME); |
| 391 | extensions.push_back(VK_KHR_8BIT_STORAGE_EXTENSION_NAME); | 399 | extensions.push_back(VK_KHR_8BIT_STORAGE_EXTENSION_NAME); |
| @@ -397,18 +405,22 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami | |||
| 397 | 405 | ||
| 398 | [[maybe_unused]] const bool nsight = | 406 | [[maybe_unused]] const bool nsight = |
| 399 | std::getenv("NVTX_INJECTION64_PATH") || std::getenv("NSIGHT_LAUNCHED"); | 407 | std::getenv("NVTX_INJECTION64_PATH") || std::getenv("NSIGHT_LAUNCHED"); |
| 400 | bool khr_shader_float16_int8{}; | 408 | bool has_khr_shader_float16_int8{}; |
| 401 | bool ext_subgroup_size_control{}; | 409 | bool has_ext_subgroup_size_control{}; |
| 410 | bool has_ext_transform_feedback{}; | ||
| 402 | for (const auto& extension : physical.enumerateDeviceExtensionProperties(nullptr, dldi)) { | 411 | for (const auto& extension : physical.enumerateDeviceExtensionProperties(nullptr, dldi)) { |
| 403 | Test(extension, khr_uniform_buffer_standard_layout, | 412 | Test(extension, khr_uniform_buffer_standard_layout, |
| 404 | VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME, true); | 413 | VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME, true); |
| 405 | Test(extension, khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false); | 414 | Test(extension, has_khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, |
| 415 | false); | ||
| 406 | Test(extension, ext_depth_range_unrestricted, | 416 | Test(extension, ext_depth_range_unrestricted, |
| 407 | VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME, true); | 417 | VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME, true); |
| 408 | Test(extension, ext_index_type_uint8, VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME, true); | 418 | Test(extension, ext_index_type_uint8, VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME, true); |
| 409 | Test(extension, ext_shader_viewport_index_layer, | 419 | Test(extension, ext_shader_viewport_index_layer, |
| 410 | VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME, true); | 420 | VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME, true); |
| 411 | Test(extension, ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, | 421 | Test(extension, has_ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, |
| 422 | false); | ||
| 423 | Test(extension, has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, | ||
| 412 | false); | 424 | false); |
| 413 | if (Settings::values.renderer_debug) { | 425 | if (Settings::values.renderer_debug) { |
| 414 | Test(extension, nv_device_diagnostic_checkpoints, | 426 | Test(extension, nv_device_diagnostic_checkpoints, |
| @@ -416,13 +428,13 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami | |||
| 416 | } | 428 | } |
| 417 | } | 429 | } |
| 418 | 430 | ||
| 419 | if (khr_shader_float16_int8) { | 431 | if (has_khr_shader_float16_int8) { |
| 420 | is_float16_supported = | 432 | is_float16_supported = |
| 421 | GetFeatures<vk::PhysicalDeviceFloat16Int8FeaturesKHR>(physical, dldi).shaderFloat16; | 433 | GetFeatures<vk::PhysicalDeviceFloat16Int8FeaturesKHR>(physical, dldi).shaderFloat16; |
| 422 | extensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME); | 434 | extensions.push_back(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME); |
| 423 | } | 435 | } |
| 424 | 436 | ||
| 425 | if (ext_subgroup_size_control) { | 437 | if (has_ext_subgroup_size_control) { |
| 426 | const auto features = | 438 | const auto features = |
| 427 | GetFeatures<vk::PhysicalDeviceSubgroupSizeControlFeaturesEXT>(physical, dldi); | 439 | GetFeatures<vk::PhysicalDeviceSubgroupSizeControlFeaturesEXT>(physical, dldi); |
| 428 | const auto properties = | 440 | const auto properties = |
| @@ -439,6 +451,20 @@ std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynami | |||
| 439 | is_warp_potentially_bigger = true; | 451 | is_warp_potentially_bigger = true; |
| 440 | } | 452 | } |
| 441 | 453 | ||
| 454 | if (has_ext_transform_feedback) { | ||
| 455 | const auto features = | ||
| 456 | GetFeatures<vk::PhysicalDeviceTransformFeedbackFeaturesEXT>(physical, dldi); | ||
| 457 | const auto properties = | ||
| 458 | GetProperties<vk::PhysicalDeviceTransformFeedbackPropertiesEXT>(physical, dldi); | ||
| 459 | |||
| 460 | if (features.transformFeedback && features.geometryStreams && | ||
| 461 | properties.maxTransformFeedbackStreams >= 4 && properties.maxTransformFeedbackBuffers && | ||
| 462 | properties.transformFeedbackQueries && properties.transformFeedbackDraw) { | ||
| 463 | extensions.push_back(VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME); | ||
| 464 | ext_transform_feedback = true; | ||
| 465 | } | ||
| 466 | } | ||
| 467 | |||
| 442 | return extensions; | 468 | return extensions; |
| 443 | } | 469 | } |
| 444 | 470 | ||
| @@ -467,8 +493,7 @@ void VKDevice::SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceK | |||
| 467 | 493 | ||
| 468 | void VKDevice::SetupFeatures(const vk::DispatchLoaderDynamic& dldi) { | 494 | void VKDevice::SetupFeatures(const vk::DispatchLoaderDynamic& dldi) { |
| 469 | const auto supported_features{physical.getFeatures(dldi)}; | 495 | const auto supported_features{physical.getFeatures(dldi)}; |
| 470 | is_shader_storage_img_read_without_format_supported = | 496 | is_formatless_image_load_supported = supported_features.shaderStorageImageReadWithoutFormat; |
| 471 | supported_features.shaderStorageImageReadWithoutFormat; | ||
| 472 | is_optimal_astc_supported = IsOptimalAstcSupported(supported_features, dldi); | 497 | is_optimal_astc_supported = IsOptimalAstcSupported(supported_features, dldi); |
| 473 | } | 498 | } |
| 474 | 499 | ||
diff --git a/src/video_core/renderer_vulkan/vk_device.h b/src/video_core/renderer_vulkan/vk_device.h index 2c27ad730..6e656517f 100644 --- a/src/video_core/renderer_vulkan/vk_device.h +++ b/src/video_core/renderer_vulkan/vk_device.h | |||
| @@ -122,11 +122,6 @@ public: | |||
| 122 | return properties.limits.maxPushConstantsSize; | 122 | return properties.limits.maxPushConstantsSize; |
| 123 | } | 123 | } |
| 124 | 124 | ||
| 125 | /// Returns true if Shader storage Image Read Without Format supported. | ||
| 126 | bool IsShaderStorageImageReadWithoutFormatSupported() const { | ||
| 127 | return is_shader_storage_img_read_without_format_supported; | ||
| 128 | } | ||
| 129 | |||
| 130 | /// Returns true if ASTC is natively supported. | 125 | /// Returns true if ASTC is natively supported. |
| 131 | bool IsOptimalAstcSupported() const { | 126 | bool IsOptimalAstcSupported() const { |
| 132 | return is_optimal_astc_supported; | 127 | return is_optimal_astc_supported; |
| @@ -147,6 +142,11 @@ public: | |||
| 147 | return (guest_warp_stages & stage) != vk::ShaderStageFlags{}; | 142 | return (guest_warp_stages & stage) != vk::ShaderStageFlags{}; |
| 148 | } | 143 | } |
| 149 | 144 | ||
| 145 | /// Returns true if formatless image load is supported. | ||
| 146 | bool IsFormatlessImageLoadSupported() const { | ||
| 147 | return is_formatless_image_load_supported; | ||
| 148 | } | ||
| 149 | |||
| 150 | /// Returns true if the device supports VK_EXT_scalar_block_layout. | 150 | /// Returns true if the device supports VK_EXT_scalar_block_layout. |
| 151 | bool IsKhrUniformBufferStandardLayoutSupported() const { | 151 | bool IsKhrUniformBufferStandardLayoutSupported() const { |
| 152 | return khr_uniform_buffer_standard_layout; | 152 | return khr_uniform_buffer_standard_layout; |
| @@ -167,6 +167,11 @@ public: | |||
| 167 | return ext_shader_viewport_index_layer; | 167 | return ext_shader_viewport_index_layer; |
| 168 | } | 168 | } |
| 169 | 169 | ||
| 170 | /// Returns true if the device supports VK_EXT_transform_feedback. | ||
| 171 | bool IsExtTransformFeedbackSupported() const { | ||
| 172 | return ext_transform_feedback; | ||
| 173 | } | ||
| 174 | |||
| 170 | /// Returns true if the device supports VK_NV_device_diagnostic_checkpoints. | 175 | /// Returns true if the device supports VK_NV_device_diagnostic_checkpoints. |
| 171 | bool IsNvDeviceDiagnosticCheckpoints() const { | 176 | bool IsNvDeviceDiagnosticCheckpoints() const { |
| 172 | return nv_device_diagnostic_checkpoints; | 177 | return nv_device_diagnostic_checkpoints; |
| @@ -214,26 +219,26 @@ private: | |||
| 214 | static std::unordered_map<vk::Format, vk::FormatProperties> GetFormatProperties( | 219 | static std::unordered_map<vk::Format, vk::FormatProperties> GetFormatProperties( |
| 215 | const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical); | 220 | const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical); |
| 216 | 221 | ||
| 217 | const vk::PhysicalDevice physical; ///< Physical device. | 222 | const vk::PhysicalDevice physical; ///< Physical device. |
| 218 | vk::DispatchLoaderDynamic dld; ///< Device function pointers. | 223 | vk::DispatchLoaderDynamic dld; ///< Device function pointers. |
| 219 | vk::PhysicalDeviceProperties properties; ///< Device properties. | 224 | vk::PhysicalDeviceProperties properties; ///< Device properties. |
| 220 | UniqueDevice logical; ///< Logical device. | 225 | UniqueDevice logical; ///< Logical device. |
| 221 | vk::Queue graphics_queue; ///< Main graphics queue. | 226 | vk::Queue graphics_queue; ///< Main graphics queue. |
| 222 | vk::Queue present_queue; ///< Main present queue. | 227 | vk::Queue present_queue; ///< Main present queue. |
| 223 | u32 graphics_family{}; ///< Main graphics queue family index. | 228 | u32 graphics_family{}; ///< Main graphics queue family index. |
| 224 | u32 present_family{}; ///< Main present queue family index. | 229 | u32 present_family{}; ///< Main present queue family index. |
| 225 | vk::DriverIdKHR driver_id{}; ///< Driver ID. | 230 | vk::DriverIdKHR driver_id{}; ///< Driver ID. |
| 226 | vk::ShaderStageFlags guest_warp_stages{}; ///< Stages where the guest warp size can be forced. | 231 | vk::ShaderStageFlags guest_warp_stages{}; ///< Stages where the guest warp size can be forced.ed |
| 227 | bool is_optimal_astc_supported{}; ///< Support for native ASTC. | 232 | bool is_optimal_astc_supported{}; ///< Support for native ASTC. |
| 228 | bool is_float16_supported{}; ///< Support for float16 arithmetics. | 233 | bool is_float16_supported{}; ///< Support for float16 arithmetics. |
| 229 | bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest. | 234 | bool is_warp_potentially_bigger{}; ///< Host warp size can be bigger than guest. |
| 235 | bool is_formatless_image_load_supported{}; ///< Support for shader image read without format. | ||
| 230 | bool khr_uniform_buffer_standard_layout{}; ///< Support for std430 on UBOs. | 236 | bool khr_uniform_buffer_standard_layout{}; ///< Support for std430 on UBOs. |
| 231 | bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8. | 237 | bool ext_index_type_uint8{}; ///< Support for VK_EXT_index_type_uint8. |
| 232 | bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. | 238 | bool ext_depth_range_unrestricted{}; ///< Support for VK_EXT_depth_range_unrestricted. |
| 233 | bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. | 239 | bool ext_shader_viewport_index_layer{}; ///< Support for VK_EXT_shader_viewport_index_layer. |
| 240 | bool ext_transform_feedback{}; ///< Support for VK_EXT_transform_feedback. | ||
| 234 | bool nv_device_diagnostic_checkpoints{}; ///< Support for VK_NV_device_diagnostic_checkpoints. | 241 | bool nv_device_diagnostic_checkpoints{}; ///< Support for VK_NV_device_diagnostic_checkpoints. |
| 235 | bool is_shader_storage_img_read_without_format_supported{}; ///< Support for shader storage | ||
| 236 | ///< image read without format | ||
| 237 | 242 | ||
| 238 | // Telemetry parameters | 243 | // Telemetry parameters |
| 239 | std::string vendor_name; ///< Device's driver name. | 244 | std::string vendor_name; ///< Device's driver name. |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index ebf85f311..056ef495c 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | |||
| @@ -273,9 +273,9 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach | |||
| 273 | specialization.workgroup_size = key.workgroup_size; | 273 | specialization.workgroup_size = key.workgroup_size; |
| 274 | specialization.shared_memory_size = key.shared_memory_size; | 274 | specialization.shared_memory_size = key.shared_memory_size; |
| 275 | 275 | ||
| 276 | const SPIRVShader spirv_shader{ | 276 | const SPIRVShader spirv_shader{Decompile(device, shader->GetIR(), ShaderType::Compute, |
| 277 | Decompile(device, shader->GetIR(), ShaderType::Compute, specialization), | 277 | shader->GetRegistry(), specialization), |
| 278 | shader->GetEntries()}; | 278 | shader->GetEntries()}; |
| 279 | entry = std::make_unique<VKComputePipeline>(device, scheduler, descriptor_pool, | 279 | entry = std::make_unique<VKComputePipeline>(device, scheduler, descriptor_pool, |
| 280 | update_descriptor_queue, spirv_shader); | 280 | update_descriptor_queue, spirv_shader); |
| 281 | return *entry; | 281 | return *entry; |
| @@ -324,8 +324,7 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) { | |||
| 324 | const auto& gpu = system.GPU().Maxwell3D(); | 324 | const auto& gpu = system.GPU().Maxwell3D(); |
| 325 | 325 | ||
| 326 | Specialization specialization; | 326 | Specialization specialization; |
| 327 | specialization.primitive_topology = fixed_state.input_assembly.topology; | 327 | if (fixed_state.input_assembly.topology == Maxwell::PrimitiveTopology::Points) { |
| 328 | if (specialization.primitive_topology == Maxwell::PrimitiveTopology::Points) { | ||
| 329 | ASSERT(fixed_state.input_assembly.point_size != 0.0f); | 328 | ASSERT(fixed_state.input_assembly.point_size != 0.0f); |
| 330 | specialization.point_size = fixed_state.input_assembly.point_size; | 329 | specialization.point_size = fixed_state.input_assembly.point_size; |
| 331 | } | 330 | } |
| @@ -333,9 +332,6 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) { | |||
| 333 | specialization.attribute_types[i] = fixed_state.vertex_input.attributes[i].type; | 332 | specialization.attribute_types[i] = fixed_state.vertex_input.attributes[i].type; |
| 334 | } | 333 | } |
| 335 | specialization.ndc_minus_one_to_one = fixed_state.rasterizer.ndc_minus_one_to_one; | 334 | specialization.ndc_minus_one_to_one = fixed_state.rasterizer.ndc_minus_one_to_one; |
| 336 | specialization.tessellation.primitive = fixed_state.tessellation.primitive; | ||
| 337 | specialization.tessellation.spacing = fixed_state.tessellation.spacing; | ||
| 338 | specialization.tessellation.clockwise = fixed_state.tessellation.clockwise; | ||
| 339 | 335 | ||
| 340 | SPIRVProgram program; | 336 | SPIRVProgram program; |
| 341 | std::vector<vk::DescriptorSetLayoutBinding> bindings; | 337 | std::vector<vk::DescriptorSetLayoutBinding> bindings; |
| @@ -356,8 +352,9 @@ VKPipelineCache::DecompileShaders(const GraphicsPipelineCacheKey& key) { | |||
| 356 | const std::size_t stage = index == 0 ? 0 : index - 1; // Stage indices are 0 - 5 | 352 | const std::size_t stage = index == 0 ? 0 : index - 1; // Stage indices are 0 - 5 |
| 357 | const auto program_type = GetShaderType(program_enum); | 353 | const auto program_type = GetShaderType(program_enum); |
| 358 | const auto& entries = shader->GetEntries(); | 354 | const auto& entries = shader->GetEntries(); |
| 359 | program[stage] = {Decompile(device, shader->GetIR(), program_type, specialization), | 355 | program[stage] = { |
| 360 | entries}; | 356 | Decompile(device, shader->GetIR(), program_type, shader->GetRegistry(), specialization), |
| 357 | entries}; | ||
| 361 | 358 | ||
| 362 | if (program_enum == Maxwell::ShaderProgram::VertexA) { | 359 | if (program_enum == Maxwell::ShaderProgram::VertexA) { |
| 363 | // VertexB was combined with VertexA, so we skip the VertexB iteration | 360 | // VertexB was combined with VertexA, so we skip the VertexB iteration |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h index e292526bb..21340c9a4 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h | |||
| @@ -132,6 +132,10 @@ public: | |||
| 132 | return shader_ir; | 132 | return shader_ir; |
| 133 | } | 133 | } |
| 134 | 134 | ||
| 135 | const VideoCommon::Shader::Registry& GetRegistry() const { | ||
| 136 | return registry; | ||
| 137 | } | ||
| 138 | |||
| 135 | const VideoCommon::Shader::ShaderIR& GetIR() const { | 139 | const VideoCommon::Shader::ShaderIR& GetIR() const { |
| 136 | return shader_ir; | 140 | return shader_ir; |
| 137 | } | 141 | } |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 2bcb17b56..f889019c1 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -347,6 +347,8 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { | |||
| 347 | [&pipeline](auto cmdbuf, auto& dld) { cmdbuf.setCheckpointNV(&pipeline, dld); }); | 347 | [&pipeline](auto cmdbuf, auto& dld) { cmdbuf.setCheckpointNV(&pipeline, dld); }); |
| 348 | } | 348 | } |
| 349 | 349 | ||
| 350 | BeginTransformFeedback(); | ||
| 351 | |||
| 350 | const auto pipeline_layout = pipeline.GetLayout(); | 352 | const auto pipeline_layout = pipeline.GetLayout(); |
| 351 | const auto descriptor_set = pipeline.CommitDescriptorSet(); | 353 | const auto descriptor_set = pipeline.CommitDescriptorSet(); |
| 352 | scheduler.Record([pipeline_layout, descriptor_set, draw_params](auto cmdbuf, auto& dld) { | 354 | scheduler.Record([pipeline_layout, descriptor_set, draw_params](auto cmdbuf, auto& dld) { |
| @@ -356,6 +358,8 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) { | |||
| 356 | } | 358 | } |
| 357 | draw_params.Draw(cmdbuf, dld); | 359 | draw_params.Draw(cmdbuf, dld); |
| 358 | }); | 360 | }); |
| 361 | |||
| 362 | EndTransformFeedback(); | ||
| 359 | } | 363 | } |
| 360 | 364 | ||
| 361 | void RasterizerVulkan::Clear() { | 365 | void RasterizerVulkan::Clear() { |
| @@ -738,6 +742,44 @@ void RasterizerVulkan::UpdateDynamicStates() { | |||
| 738 | UpdateStencilFaces(regs); | 742 | UpdateStencilFaces(regs); |
| 739 | } | 743 | } |
| 740 | 744 | ||
| 745 | void RasterizerVulkan::BeginTransformFeedback() { | ||
| 746 | const auto& regs = system.GPU().Maxwell3D().regs; | ||
| 747 | if (regs.tfb_enabled == 0) { | ||
| 748 | return; | ||
| 749 | } | ||
| 750 | |||
| 751 | UNIMPLEMENTED_IF(regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationControl) || | ||
| 752 | regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::TesselationEval) || | ||
| 753 | regs.IsShaderConfigEnabled(Maxwell::ShaderProgram::Geometry)); | ||
| 754 | |||
| 755 | UNIMPLEMENTED_IF(regs.tfb_bindings[1].buffer_enable); | ||
| 756 | UNIMPLEMENTED_IF(regs.tfb_bindings[2].buffer_enable); | ||
| 757 | UNIMPLEMENTED_IF(regs.tfb_bindings[3].buffer_enable); | ||
| 758 | |||
| 759 | const auto& binding = regs.tfb_bindings[0]; | ||
| 760 | UNIMPLEMENTED_IF(binding.buffer_enable == 0); | ||
| 761 | UNIMPLEMENTED_IF(binding.buffer_offset != 0); | ||
| 762 | |||
| 763 | const GPUVAddr gpu_addr = binding.Address(); | ||
| 764 | const std::size_t size = binding.buffer_size; | ||
| 765 | const auto [buffer, offset] = buffer_cache.UploadMemory(gpu_addr, size, 4, true); | ||
| 766 | |||
| 767 | scheduler.Record([buffer = *buffer, offset = offset, size](auto cmdbuf, auto& dld) { | ||
| 768 | cmdbuf.bindTransformFeedbackBuffersEXT(0, {buffer}, {offset}, {size}, dld); | ||
| 769 | cmdbuf.beginTransformFeedbackEXT(0, {}, {}, dld); | ||
| 770 | }); | ||
| 771 | } | ||
| 772 | |||
| 773 | void RasterizerVulkan::EndTransformFeedback() { | ||
| 774 | const auto& regs = system.GPU().Maxwell3D().regs; | ||
| 775 | if (regs.tfb_enabled == 0) { | ||
| 776 | return; | ||
| 777 | } | ||
| 778 | |||
| 779 | scheduler.Record( | ||
| 780 | [](auto cmdbuf, auto& dld) { cmdbuf.endTransformFeedbackEXT(0, {}, {}, dld); }); | ||
| 781 | } | ||
| 782 | |||
| 741 | void RasterizerVulkan::SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input, | 783 | void RasterizerVulkan::SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input, |
| 742 | BufferBindings& buffer_bindings) { | 784 | BufferBindings& buffer_bindings) { |
| 743 | const auto& regs = system.GPU().Maxwell3D().regs; | 785 | const auto& regs = system.GPU().Maxwell3D().regs; |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 96ea05f0a..b2e73d98d 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h | |||
| @@ -169,6 +169,10 @@ private: | |||
| 169 | 169 | ||
| 170 | void UpdateDynamicStates(); | 170 | void UpdateDynamicStates(); |
| 171 | 171 | ||
| 172 | void BeginTransformFeedback(); | ||
| 173 | |||
| 174 | void EndTransformFeedback(); | ||
| 175 | |||
| 172 | bool WalkAttachmentOverlaps(const CachedSurfaceView& attachment); | 176 | bool WalkAttachmentOverlaps(const CachedSurfaceView& attachment); |
| 173 | 177 | ||
| 174 | void SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input, | 178 | void SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input, |
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp index cfcca5af0..b2c298051 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp | |||
| @@ -5,7 +5,9 @@ | |||
| 5 | #include <functional> | 5 | #include <functional> |
| 6 | #include <limits> | 6 | #include <limits> |
| 7 | #include <map> | 7 | #include <map> |
| 8 | #include <optional> | ||
| 8 | #include <type_traits> | 9 | #include <type_traits> |
| 10 | #include <unordered_map> | ||
| 9 | #include <utility> | 11 | #include <utility> |
| 10 | 12 | ||
| 11 | #include <fmt/format.h> | 13 | #include <fmt/format.h> |
| @@ -24,6 +26,7 @@ | |||
| 24 | #include "video_core/renderer_vulkan/vk_shader_decompiler.h" | 26 | #include "video_core/renderer_vulkan/vk_shader_decompiler.h" |
| 25 | #include "video_core/shader/node.h" | 27 | #include "video_core/shader/node.h" |
| 26 | #include "video_core/shader/shader_ir.h" | 28 | #include "video_core/shader/shader_ir.h" |
| 29 | #include "video_core/shader/transform_feedback.h" | ||
| 27 | 30 | ||
| 28 | namespace Vulkan { | 31 | namespace Vulkan { |
| 29 | 32 | ||
| @@ -93,6 +96,12 @@ struct VertexIndices { | |||
| 93 | std::optional<u32> clip_distances; | 96 | std::optional<u32> clip_distances; |
| 94 | }; | 97 | }; |
| 95 | 98 | ||
| 99 | struct GenericVaryingDescription { | ||
| 100 | Id id = nullptr; | ||
| 101 | u32 first_element = 0; | ||
| 102 | bool is_scalar = false; | ||
| 103 | }; | ||
| 104 | |||
| 96 | spv::Dim GetSamplerDim(const Sampler& sampler) { | 105 | spv::Dim GetSamplerDim(const Sampler& sampler) { |
| 97 | ASSERT(!sampler.IsBuffer()); | 106 | ASSERT(!sampler.IsBuffer()); |
| 98 | switch (sampler.GetType()) { | 107 | switch (sampler.GetType()) { |
| @@ -266,9 +275,13 @@ bool IsPrecise(Operation operand) { | |||
| 266 | class SPIRVDecompiler final : public Sirit::Module { | 275 | class SPIRVDecompiler final : public Sirit::Module { |
| 267 | public: | 276 | public: |
| 268 | explicit SPIRVDecompiler(const VKDevice& device, const ShaderIR& ir, ShaderType stage, | 277 | explicit SPIRVDecompiler(const VKDevice& device, const ShaderIR& ir, ShaderType stage, |
| 269 | const Specialization& specialization) | 278 | const Registry& registry, const Specialization& specialization) |
| 270 | : Module(0x00010300), device{device}, ir{ir}, stage{stage}, header{ir.GetHeader()}, | 279 | : Module(0x00010300), device{device}, ir{ir}, stage{stage}, header{ir.GetHeader()}, |
| 271 | specialization{specialization} { | 280 | registry{registry}, specialization{specialization} { |
| 281 | if (stage != ShaderType::Compute) { | ||
| 282 | transform_feedback = BuildTransformFeedback(registry.GetGraphicsInfo()); | ||
| 283 | } | ||
| 284 | |||
| 272 | AddCapability(spv::Capability::Shader); | 285 | AddCapability(spv::Capability::Shader); |
| 273 | AddCapability(spv::Capability::UniformAndStorageBuffer16BitAccess); | 286 | AddCapability(spv::Capability::UniformAndStorageBuffer16BitAccess); |
| 274 | AddCapability(spv::Capability::ImageQuery); | 287 | AddCapability(spv::Capability::ImageQuery); |
| @@ -286,6 +299,15 @@ public: | |||
| 286 | AddExtension("SPV_KHR_variable_pointers"); | 299 | AddExtension("SPV_KHR_variable_pointers"); |
| 287 | AddExtension("SPV_KHR_shader_draw_parameters"); | 300 | AddExtension("SPV_KHR_shader_draw_parameters"); |
| 288 | 301 | ||
| 302 | if (!transform_feedback.empty()) { | ||
| 303 | if (device.IsExtTransformFeedbackSupported()) { | ||
| 304 | AddCapability(spv::Capability::TransformFeedback); | ||
| 305 | } else { | ||
| 306 | LOG_ERROR(Render_Vulkan, "Shader requires transform feedbacks but these are not " | ||
| 307 | "supported on this device"); | ||
| 308 | } | ||
| 309 | } | ||
| 310 | |||
| 289 | if (ir.UsesLayer() || ir.UsesViewportIndex()) { | 311 | if (ir.UsesLayer() || ir.UsesViewportIndex()) { |
| 290 | if (ir.UsesViewportIndex()) { | 312 | if (ir.UsesViewportIndex()) { |
| 291 | AddCapability(spv::Capability::MultiViewport); | 313 | AddCapability(spv::Capability::MultiViewport); |
| @@ -296,7 +318,7 @@ public: | |||
| 296 | } | 318 | } |
| 297 | } | 319 | } |
| 298 | 320 | ||
| 299 | if (device.IsShaderStorageImageReadWithoutFormatSupported()) { | 321 | if (device.IsFormatlessImageLoadSupported()) { |
| 300 | AddCapability(spv::Capability::StorageImageReadWithoutFormat); | 322 | AddCapability(spv::Capability::StorageImageReadWithoutFormat); |
| 301 | } | 323 | } |
| 302 | 324 | ||
| @@ -318,25 +340,29 @@ public: | |||
| 318 | AddExecutionMode(main, spv::ExecutionMode::OutputVertices, | 340 | AddExecutionMode(main, spv::ExecutionMode::OutputVertices, |
| 319 | header.common2.threads_per_input_primitive); | 341 | header.common2.threads_per_input_primitive); |
| 320 | break; | 342 | break; |
| 321 | case ShaderType::TesselationEval: | 343 | case ShaderType::TesselationEval: { |
| 344 | const auto& info = registry.GetGraphicsInfo(); | ||
| 322 | AddCapability(spv::Capability::Tessellation); | 345 | AddCapability(spv::Capability::Tessellation); |
| 323 | AddEntryPoint(spv::ExecutionModel::TessellationEvaluation, main, "main", interfaces); | 346 | AddEntryPoint(spv::ExecutionModel::TessellationEvaluation, main, "main", interfaces); |
| 324 | AddExecutionMode(main, GetExecutionMode(specialization.tessellation.primitive)); | 347 | AddExecutionMode(main, GetExecutionMode(info.tessellation_primitive)); |
| 325 | AddExecutionMode(main, GetExecutionMode(specialization.tessellation.spacing)); | 348 | AddExecutionMode(main, GetExecutionMode(info.tessellation_spacing)); |
| 326 | AddExecutionMode(main, specialization.tessellation.clockwise | 349 | AddExecutionMode(main, info.tessellation_clockwise |
| 327 | ? spv::ExecutionMode::VertexOrderCw | 350 | ? spv::ExecutionMode::VertexOrderCw |
| 328 | : spv::ExecutionMode::VertexOrderCcw); | 351 | : spv::ExecutionMode::VertexOrderCcw); |
| 329 | break; | 352 | break; |
| 330 | case ShaderType::Geometry: | 353 | } |
| 354 | case ShaderType::Geometry: { | ||
| 355 | const auto& info = registry.GetGraphicsInfo(); | ||
| 331 | AddCapability(spv::Capability::Geometry); | 356 | AddCapability(spv::Capability::Geometry); |
| 332 | AddEntryPoint(spv::ExecutionModel::Geometry, main, "main", interfaces); | 357 | AddEntryPoint(spv::ExecutionModel::Geometry, main, "main", interfaces); |
| 333 | AddExecutionMode(main, GetExecutionMode(specialization.primitive_topology)); | 358 | AddExecutionMode(main, GetExecutionMode(info.primitive_topology)); |
| 334 | AddExecutionMode(main, GetExecutionMode(header.common3.output_topology)); | 359 | AddExecutionMode(main, GetExecutionMode(header.common3.output_topology)); |
| 335 | AddExecutionMode(main, spv::ExecutionMode::OutputVertices, | 360 | AddExecutionMode(main, spv::ExecutionMode::OutputVertices, |
| 336 | header.common4.max_output_vertices); | 361 | header.common4.max_output_vertices); |
| 337 | // TODO(Rodrigo): Where can we get this info from? | 362 | // TODO(Rodrigo): Where can we get this info from? |
| 338 | AddExecutionMode(main, spv::ExecutionMode::Invocations, 1U); | 363 | AddExecutionMode(main, spv::ExecutionMode::Invocations, 1U); |
| 339 | break; | 364 | break; |
| 365 | } | ||
| 340 | case ShaderType::Fragment: | 366 | case ShaderType::Fragment: |
| 341 | AddEntryPoint(spv::ExecutionModel::Fragment, main, "main", interfaces); | 367 | AddEntryPoint(spv::ExecutionModel::Fragment, main, "main", interfaces); |
| 342 | AddExecutionMode(main, spv::ExecutionMode::OriginUpperLeft); | 368 | AddExecutionMode(main, spv::ExecutionMode::OriginUpperLeft); |
| @@ -545,7 +571,8 @@ private: | |||
| 545 | if (stage != ShaderType::Geometry) { | 571 | if (stage != ShaderType::Geometry) { |
| 546 | return; | 572 | return; |
| 547 | } | 573 | } |
| 548 | const u32 num_input = GetNumPrimitiveTopologyVertices(specialization.primitive_topology); | 574 | const auto& info = registry.GetGraphicsInfo(); |
| 575 | const u32 num_input = GetNumPrimitiveTopologyVertices(info.primitive_topology); | ||
| 549 | DeclareInputVertexArray(num_input); | 576 | DeclareInputVertexArray(num_input); |
| 550 | DeclareOutputVertex(); | 577 | DeclareOutputVertex(); |
| 551 | } | 578 | } |
| @@ -742,12 +769,34 @@ private: | |||
| 742 | } | 769 | } |
| 743 | 770 | ||
| 744 | void DeclareOutputAttributes() { | 771 | void DeclareOutputAttributes() { |
| 772 | if (stage == ShaderType::Compute || stage == ShaderType::Fragment) { | ||
| 773 | return; | ||
| 774 | } | ||
| 775 | |||
| 776 | UNIMPLEMENTED_IF(registry.GetGraphicsInfo().tfb_enabled && stage != ShaderType::Vertex); | ||
| 745 | for (const auto index : ir.GetOutputAttributes()) { | 777 | for (const auto index : ir.GetOutputAttributes()) { |
| 746 | if (!IsGenericAttribute(index)) { | 778 | if (!IsGenericAttribute(index)) { |
| 747 | continue; | 779 | continue; |
| 748 | } | 780 | } |
| 749 | const u32 location = GetGenericAttributeLocation(index); | 781 | DeclareOutputAttribute(index); |
| 750 | Id type = t_float4; | 782 | } |
| 783 | } | ||
| 784 | |||
| 785 | void DeclareOutputAttribute(Attribute::Index index) { | ||
| 786 | static constexpr std::string_view swizzle = "xyzw"; | ||
| 787 | |||
| 788 | const u32 location = GetGenericAttributeLocation(index); | ||
| 789 | u8 element = 0; | ||
| 790 | while (element < 4) { | ||
| 791 | const std::size_t remainder = 4 - element; | ||
| 792 | |||
| 793 | std::size_t num_components = remainder; | ||
| 794 | const std::optional tfb = GetTransformFeedbackInfo(index, element); | ||
| 795 | if (tfb) { | ||
| 796 | num_components = tfb->components; | ||
| 797 | } | ||
| 798 | |||
| 799 | Id type = GetTypeVectorDefinitionLut(Type::Float).at(num_components - 1); | ||
| 751 | Id varying_default = v_varying_default; | 800 | Id varying_default = v_varying_default; |
| 752 | if (IsOutputAttributeArray()) { | 801 | if (IsOutputAttributeArray()) { |
| 753 | const u32 num = GetNumOutputVertices(); | 802 | const u32 num = GetNumOutputVertices(); |
| @@ -760,13 +809,45 @@ private: | |||
| 760 | } | 809 | } |
| 761 | type = TypePointer(spv::StorageClass::Output, type); | 810 | type = TypePointer(spv::StorageClass::Output, type); |
| 762 | 811 | ||
| 812 | std::string name = fmt::format("out_attr{}", location); | ||
| 813 | if (num_components < 4 || element > 0) { | ||
| 814 | name = fmt::format("{}_{}", name, swizzle.substr(element, num_components)); | ||
| 815 | } | ||
| 816 | |||
| 763 | const Id id = OpVariable(type, spv::StorageClass::Output, varying_default); | 817 | const Id id = OpVariable(type, spv::StorageClass::Output, varying_default); |
| 764 | Name(AddGlobalVariable(id), fmt::format("out_attr{}", location)); | 818 | Name(AddGlobalVariable(id), name); |
| 765 | output_attributes.emplace(index, id); | 819 | |
| 820 | GenericVaryingDescription description; | ||
| 821 | description.id = id; | ||
| 822 | description.first_element = element; | ||
| 823 | description.is_scalar = num_components == 1; | ||
| 824 | for (u32 i = 0; i < num_components; ++i) { | ||
| 825 | const u8 offset = static_cast<u8>(static_cast<u32>(index) * 4 + element + i); | ||
| 826 | output_attributes.emplace(offset, description); | ||
| 827 | } | ||
| 766 | interfaces.push_back(id); | 828 | interfaces.push_back(id); |
| 767 | 829 | ||
| 768 | Decorate(id, spv::Decoration::Location, location); | 830 | Decorate(id, spv::Decoration::Location, location); |
| 831 | if (element > 0) { | ||
| 832 | Decorate(id, spv::Decoration::Component, static_cast<u32>(element)); | ||
| 833 | } | ||
| 834 | if (tfb && device.IsExtTransformFeedbackSupported()) { | ||
| 835 | Decorate(id, spv::Decoration::XfbBuffer, static_cast<u32>(tfb->buffer)); | ||
| 836 | Decorate(id, spv::Decoration::XfbStride, static_cast<u32>(tfb->stride)); | ||
| 837 | Decorate(id, spv::Decoration::Offset, static_cast<u32>(tfb->offset)); | ||
| 838 | } | ||
| 839 | |||
| 840 | element += static_cast<u8>(num_components); | ||
| 841 | } | ||
| 842 | } | ||
| 843 | |||
| 844 | std::optional<VaryingTFB> GetTransformFeedbackInfo(Attribute::Index index, u8 element = 0) { | ||
| 845 | const u8 location = static_cast<u8>(static_cast<u32>(index) * 4 + element); | ||
| 846 | const auto it = transform_feedback.find(location); | ||
| 847 | if (it == transform_feedback.end()) { | ||
| 848 | return {}; | ||
| 769 | } | 849 | } |
| 850 | return it->second; | ||
| 770 | } | 851 | } |
| 771 | 852 | ||
| 772 | u32 DeclareConstantBuffers(u32 binding) { | 853 | u32 DeclareConstantBuffers(u32 binding) { |
| @@ -898,7 +979,7 @@ private: | |||
| 898 | u32 GetNumInputVertices() const { | 979 | u32 GetNumInputVertices() const { |
| 899 | switch (stage) { | 980 | switch (stage) { |
| 900 | case ShaderType::Geometry: | 981 | case ShaderType::Geometry: |
| 901 | return GetNumPrimitiveTopologyVertices(specialization.primitive_topology); | 982 | return GetNumPrimitiveTopologyVertices(registry.GetGraphicsInfo().primitive_topology); |
| 902 | case ShaderType::TesselationControl: | 983 | case ShaderType::TesselationControl: |
| 903 | case ShaderType::TesselationEval: | 984 | case ShaderType::TesselationEval: |
| 904 | return NumInputPatches; | 985 | return NumInputPatches; |
| @@ -1346,8 +1427,14 @@ private: | |||
| 1346 | } | 1427 | } |
| 1347 | default: | 1428 | default: |
| 1348 | if (IsGenericAttribute(attribute)) { | 1429 | if (IsGenericAttribute(attribute)) { |
| 1349 | const Id composite = output_attributes.at(attribute); | 1430 | const u8 offset = static_cast<u8>(static_cast<u8>(attribute) * 4 + element); |
| 1350 | return {ArrayPass(t_out_float, composite, {element}), Type::Float}; | 1431 | const GenericVaryingDescription description = output_attributes.at(offset); |
| 1432 | const Id composite = description.id; | ||
| 1433 | std::vector<u32> indices; | ||
| 1434 | if (!description.is_scalar) { | ||
| 1435 | indices.push_back(element - description.first_element); | ||
| 1436 | } | ||
| 1437 | return {ArrayPass(t_out_float, composite, indices), Type::Float}; | ||
| 1351 | } | 1438 | } |
| 1352 | UNIMPLEMENTED_MSG("Unhandled output attribute: {}", | 1439 | UNIMPLEMENTED_MSG("Unhandled output attribute: {}", |
| 1353 | static_cast<u32>(attribute)); | 1440 | static_cast<u32>(attribute)); |
| @@ -1793,7 +1880,7 @@ private: | |||
| 1793 | } | 1880 | } |
| 1794 | 1881 | ||
| 1795 | Expression ImageLoad(Operation operation) { | 1882 | Expression ImageLoad(Operation operation) { |
| 1796 | if (!device.IsShaderStorageImageReadWithoutFormatSupported()) { | 1883 | if (!device.IsFormatlessImageLoadSupported()) { |
| 1797 | return {v_float_zero, Type::Float}; | 1884 | return {v_float_zero, Type::Float}; |
| 1798 | } | 1885 | } |
| 1799 | 1886 | ||
| @@ -2258,11 +2345,11 @@ private: | |||
| 2258 | std::array<Id, 4> GetTypeVectorDefinitionLut(Type type) const { | 2345 | std::array<Id, 4> GetTypeVectorDefinitionLut(Type type) const { |
| 2259 | switch (type) { | 2346 | switch (type) { |
| 2260 | case Type::Float: | 2347 | case Type::Float: |
| 2261 | return {nullptr, t_float2, t_float3, t_float4}; | 2348 | return {t_float, t_float2, t_float3, t_float4}; |
| 2262 | case Type::Int: | 2349 | case Type::Int: |
| 2263 | return {nullptr, t_int2, t_int3, t_int4}; | 2350 | return {t_int, t_int2, t_int3, t_int4}; |
| 2264 | case Type::Uint: | 2351 | case Type::Uint: |
| 2265 | return {nullptr, t_uint2, t_uint3, t_uint4}; | 2352 | return {t_uint, t_uint2, t_uint3, t_uint4}; |
| 2266 | default: | 2353 | default: |
| 2267 | UNIMPLEMENTED(); | 2354 | UNIMPLEMENTED(); |
| 2268 | return {}; | 2355 | return {}; |
| @@ -2495,7 +2582,9 @@ private: | |||
| 2495 | const ShaderIR& ir; | 2582 | const ShaderIR& ir; |
| 2496 | const ShaderType stage; | 2583 | const ShaderType stage; |
| 2497 | const Tegra::Shader::Header header; | 2584 | const Tegra::Shader::Header header; |
| 2585 | const Registry& registry; | ||
| 2498 | const Specialization& specialization; | 2586 | const Specialization& specialization; |
| 2587 | std::unordered_map<u8, VaryingTFB> transform_feedback; | ||
| 2499 | 2588 | ||
| 2500 | const Id t_void = Name(TypeVoid(), "void"); | 2589 | const Id t_void = Name(TypeVoid(), "void"); |
| 2501 | 2590 | ||
| @@ -2584,7 +2673,7 @@ private: | |||
| 2584 | Id shared_memory{}; | 2673 | Id shared_memory{}; |
| 2585 | std::array<Id, INTERNAL_FLAGS_COUNT> internal_flags{}; | 2674 | std::array<Id, INTERNAL_FLAGS_COUNT> internal_flags{}; |
| 2586 | std::map<Attribute::Index, Id> input_attributes; | 2675 | std::map<Attribute::Index, Id> input_attributes; |
| 2587 | std::map<Attribute::Index, Id> output_attributes; | 2676 | std::unordered_map<u8, GenericVaryingDescription> output_attributes; |
| 2588 | std::map<u32, Id> constant_buffers; | 2677 | std::map<u32, Id> constant_buffers; |
| 2589 | std::map<GlobalMemoryBase, Id> global_buffers; | 2678 | std::map<GlobalMemoryBase, Id> global_buffers; |
| 2590 | std::map<u32, TexelBuffer> texel_buffers; | 2679 | std::map<u32, TexelBuffer> texel_buffers; |
| @@ -2870,8 +2959,9 @@ ShaderEntries GenerateShaderEntries(const VideoCommon::Shader::ShaderIR& ir) { | |||
| 2870 | } | 2959 | } |
| 2871 | 2960 | ||
| 2872 | std::vector<u32> Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir, | 2961 | std::vector<u32> Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir, |
| 2873 | ShaderType stage, const Specialization& specialization) { | 2962 | ShaderType stage, const VideoCommon::Shader::Registry& registry, |
| 2874 | return SPIRVDecompiler(device, ir, stage, specialization).Assemble(); | 2963 | const Specialization& specialization) { |
| 2964 | return SPIRVDecompiler(device, ir, stage, registry, specialization).Assemble(); | ||
| 2875 | } | 2965 | } |
| 2876 | 2966 | ||
| 2877 | } // namespace Vulkan | 2967 | } // namespace Vulkan |
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.h b/src/video_core/renderer_vulkan/vk_shader_decompiler.h index f5dc14d9e..ffea4709e 100644 --- a/src/video_core/renderer_vulkan/vk_shader_decompiler.h +++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.h | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include "common/common_types.h" | 15 | #include "common/common_types.h" |
| 16 | #include "video_core/engines/maxwell_3d.h" | 16 | #include "video_core/engines/maxwell_3d.h" |
| 17 | #include "video_core/engines/shader_type.h" | 17 | #include "video_core/engines/shader_type.h" |
| 18 | #include "video_core/shader/registry.h" | ||
| 18 | #include "video_core/shader/shader_ir.h" | 19 | #include "video_core/shader/shader_ir.h" |
| 19 | 20 | ||
| 20 | namespace Vulkan { | 21 | namespace Vulkan { |
| @@ -91,17 +92,9 @@ struct Specialization final { | |||
| 91 | u32 shared_memory_size{}; | 92 | u32 shared_memory_size{}; |
| 92 | 93 | ||
| 93 | // Graphics specific | 94 | // Graphics specific |
| 94 | Maxwell::PrimitiveTopology primitive_topology{}; | ||
| 95 | std::optional<float> point_size{}; | 95 | std::optional<float> point_size{}; |
| 96 | std::array<Maxwell::VertexAttribute::Type, Maxwell::NumVertexAttributes> attribute_types{}; | 96 | std::array<Maxwell::VertexAttribute::Type, Maxwell::NumVertexAttributes> attribute_types{}; |
| 97 | bool ndc_minus_one_to_one{}; | 97 | bool ndc_minus_one_to_one{}; |
| 98 | |||
| 99 | // Tessellation specific | ||
| 100 | struct { | ||
| 101 | Maxwell::TessellationPrimitive primitive{}; | ||
| 102 | Maxwell::TessellationSpacing spacing{}; | ||
| 103 | bool clockwise{}; | ||
| 104 | } tessellation; | ||
| 105 | }; | 98 | }; |
| 106 | // Old gcc versions don't consider this trivially copyable. | 99 | // Old gcc versions don't consider this trivially copyable. |
| 107 | // static_assert(std::is_trivially_copyable_v<Specialization>); | 100 | // static_assert(std::is_trivially_copyable_v<Specialization>); |
| @@ -114,6 +107,8 @@ struct SPIRVShader { | |||
| 114 | ShaderEntries GenerateShaderEntries(const VideoCommon::Shader::ShaderIR& ir); | 107 | ShaderEntries GenerateShaderEntries(const VideoCommon::Shader::ShaderIR& ir); |
| 115 | 108 | ||
| 116 | std::vector<u32> Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir, | 109 | std::vector<u32> Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir, |
| 117 | Tegra::Engines::ShaderType stage, const Specialization& specialization); | 110 | Tegra::Engines::ShaderType stage, |
| 111 | const VideoCommon::Shader::Registry& registry, | ||
| 112 | const Specialization& specialization); | ||
| 118 | 113 | ||
| 119 | } // namespace Vulkan | 114 | } // namespace Vulkan |
diff --git a/src/video_core/shader/transform_feedback.cpp b/src/video_core/shader/transform_feedback.cpp new file mode 100644 index 000000000..22a933761 --- /dev/null +++ b/src/video_core/shader/transform_feedback.cpp | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <array> | ||
| 7 | #include <unordered_map> | ||
| 8 | |||
| 9 | #include "common/assert.h" | ||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "video_core/engines/maxwell_3d.h" | ||
| 12 | #include "video_core/shader/registry.h" | ||
| 13 | #include "video_core/shader/transform_feedback.h" | ||
| 14 | |||
| 15 | namespace VideoCommon::Shader { | ||
| 16 | |||
| 17 | namespace { | ||
| 18 | |||
| 19 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | ||
| 20 | |||
| 21 | // TODO(Rodrigo): Change this to constexpr std::unordered_set in C++20 | ||
| 22 | |||
| 23 | /// Attribute offsets that describe a vector | ||
| 24 | constexpr std::array VECTORS = { | ||
| 25 | 28, // gl_Position | ||
| 26 | 32, // Generic 0 | ||
| 27 | 36, // Generic 1 | ||
| 28 | 40, // Generic 2 | ||
| 29 | 44, // Generic 3 | ||
| 30 | 48, // Generic 4 | ||
| 31 | 52, // Generic 5 | ||
| 32 | 56, // Generic 6 | ||
| 33 | 60, // Generic 7 | ||
| 34 | 64, // Generic 8 | ||
| 35 | 68, // Generic 9 | ||
| 36 | 72, // Generic 10 | ||
| 37 | 76, // Generic 11 | ||
| 38 | 80, // Generic 12 | ||
| 39 | 84, // Generic 13 | ||
| 40 | 88, // Generic 14 | ||
| 41 | 92, // Generic 15 | ||
| 42 | 96, // Generic 16 | ||
| 43 | 100, // Generic 17 | ||
| 44 | 104, // Generic 18 | ||
| 45 | 108, // Generic 19 | ||
| 46 | 112, // Generic 20 | ||
| 47 | 116, // Generic 21 | ||
| 48 | 120, // Generic 22 | ||
| 49 | 124, // Generic 23 | ||
| 50 | 128, // Generic 24 | ||
| 51 | 132, // Generic 25 | ||
| 52 | 136, // Generic 26 | ||
| 53 | 140, // Generic 27 | ||
| 54 | 144, // Generic 28 | ||
| 55 | 148, // Generic 29 | ||
| 56 | 152, // Generic 30 | ||
| 57 | 156, // Generic 31 | ||
| 58 | 160, // gl_FrontColor | ||
| 59 | 164, // gl_FrontSecondaryColor | ||
| 60 | 160, // gl_BackColor | ||
| 61 | 164, // gl_BackSecondaryColor | ||
| 62 | 192, // gl_TexCoord[0] | ||
| 63 | 196, // gl_TexCoord[1] | ||
| 64 | 200, // gl_TexCoord[2] | ||
| 65 | 204, // gl_TexCoord[3] | ||
| 66 | 208, // gl_TexCoord[4] | ||
| 67 | 212, // gl_TexCoord[5] | ||
| 68 | 216, // gl_TexCoord[6] | ||
| 69 | 220, // gl_TexCoord[7] | ||
| 70 | }; | ||
| 71 | } // namespace | ||
| 72 | |||
| 73 | std::unordered_map<u8, VaryingTFB> BuildTransformFeedback(const GraphicsInfo& info) { | ||
| 74 | |||
| 75 | std::unordered_map<u8, VaryingTFB> tfb; | ||
| 76 | |||
| 77 | for (std::size_t buffer = 0; buffer < Maxwell::NumTransformFeedbackBuffers; ++buffer) { | ||
| 78 | const auto& locations = info.tfb_varying_locs[buffer]; | ||
| 79 | const auto& layout = info.tfb_layouts[buffer]; | ||
| 80 | const std::size_t varying_count = layout.varying_count; | ||
| 81 | |||
| 82 | std::size_t highest = 0; | ||
| 83 | |||
| 84 | for (std::size_t offset = 0; offset < varying_count; ++offset) { | ||
| 85 | const std::size_t base_offset = offset; | ||
| 86 | const u8 location = locations[offset]; | ||
| 87 | |||
| 88 | VaryingTFB varying; | ||
| 89 | varying.buffer = layout.stream; | ||
| 90 | varying.stride = layout.stride; | ||
| 91 | varying.offset = offset * sizeof(u32); | ||
| 92 | varying.components = 1; | ||
| 93 | |||
| 94 | if (std::find(VECTORS.begin(), VECTORS.end(), location / 4 * 4) != VECTORS.end()) { | ||
| 95 | UNIMPLEMENTED_IF_MSG(location % 4 != 0, "Unaligned TFB"); | ||
| 96 | |||
| 97 | const u8 base_index = location / 4; | ||
| 98 | while (offset + 1 < varying_count && base_index == locations[offset + 1] / 4) { | ||
| 99 | ++offset; | ||
| 100 | ++varying.components; | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | [[maybe_unused]] const bool inserted = tfb.emplace(location, varying).second; | ||
| 105 | UNIMPLEMENTED_IF_MSG(!inserted, "Varying already stored"); | ||
| 106 | |||
| 107 | highest = std::max(highest, (base_offset + varying.components) * sizeof(u32)); | ||
| 108 | } | ||
| 109 | |||
| 110 | UNIMPLEMENTED_IF(highest != layout.stride); | ||
| 111 | } | ||
| 112 | return tfb; | ||
| 113 | } | ||
| 114 | |||
| 115 | } // namespace VideoCommon::Shader | ||
diff --git a/src/video_core/shader/transform_feedback.h b/src/video_core/shader/transform_feedback.h new file mode 100644 index 000000000..77d05f64c --- /dev/null +++ b/src/video_core/shader/transform_feedback.h | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <unordered_map> | ||
| 8 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "video_core/shader/registry.h" | ||
| 11 | |||
| 12 | namespace VideoCommon::Shader { | ||
| 13 | |||
| 14 | struct VaryingTFB { | ||
| 15 | std::size_t buffer; | ||
| 16 | std::size_t stride; | ||
| 17 | std::size_t offset; | ||
| 18 | std::size_t components; | ||
| 19 | }; | ||
| 20 | |||
| 21 | std::unordered_map<u8, VaryingTFB> BuildTransformFeedback(const GraphicsInfo& info); | ||
| 22 | |||
| 23 | } // namespace VideoCommon::Shader | ||