renderer_vulkan: Begin hardware shader support
* Still experimental and works only with homebrew
This commit is contained in:
		| @@ -90,8 +90,17 @@ vk::Format ToVkAttributeFormat(VertexAttribute attrib) { | |||||||
|         case 4: |         case 4: | ||||||
|             return vk::Format::eR32G32B32A32Sfloat; |             return vk::Format::eR32G32B32A32Sfloat; | ||||||
|         } |         } | ||||||
|  |     case AttribType::Ubyte: | ||||||
|  |         switch (attrib.size) { | ||||||
|  |         case 4: | ||||||
|  |             return vk::Format::eR8G8B8A8Uint; | ||||||
|  |         default: | ||||||
|  |             fmt::print("{}\n", attrib.size.Value()); | ||||||
|  |             UNREACHABLE(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|     default: |     default: | ||||||
|         LOG_CRITICAL(Render_Vulkan, "Unimplemented vertex attribute format!"); |         LOG_CRITICAL(Render_Vulkan, "Unimplemented vertex attribute type {}", attrib.type.Value()); | ||||||
|         UNREACHABLE(); |         UNREACHABLE(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -330,14 +330,12 @@ void RasterizerVulkan::SetupVertexArray(u32 vs_input_size, u32 vs_input_index_mi | |||||||
|                                         u32 vs_input_index_max) { |                                         u32 vs_input_index_max) { | ||||||
|     auto [array_ptr, array_offset, _] = vertex_buffer.Map(vs_input_size, 4); |     auto [array_ptr, array_offset, _] = vertex_buffer.Map(vs_input_size, 4); | ||||||
|  |  | ||||||
|     /** |     // The Nintendo 3DS has 12 attribute loaders which are used to tell the GPU | ||||||
|      * The Nintendo 3DS has 12 attribute loaders which are used to tell the GPU |     // how to interpret vertex data. The program firsts sets GPUREG_ATTR_BUF_BASE to the base | ||||||
|      * how to interpret vertex data. The program firsts sets GPUREG_ATTR_BUF_BASE to the base |     // address containing the vertex array data. The data for each attribute loader (i) can be found | ||||||
|      * address containing the vertex array data. The data for each attribute loader (i) can be found |     // by adding GPUREG_ATTR_BUFi_OFFSET to the base address. Attribute loaders can be thought | ||||||
|      * by adding GPUREG_ATTR_BUFi_OFFSET to the base address. Attribute loaders can be thought |     // as something analogous to Vulkan bindings. The user can store attributes in separate loaders | ||||||
|      * as something analogous to Vulkan bindings. The user can store attributes in separate loaders |     // or interleave them in the same loader. | ||||||
|      * or interleave them in the same loader. |  | ||||||
|      */ |  | ||||||
|     const auto& regs = Pica::g_state.regs; |     const auto& regs = Pica::g_state.regs; | ||||||
|     const auto& vertex_attributes = regs.pipeline.vertex_attributes; |     const auto& vertex_attributes = regs.pipeline.vertex_attributes; | ||||||
|     PAddr base_address = vertex_attributes.GetPhysicalBaseAddress(); // GPUREG_ATTR_BUF_BASE |     PAddr base_address = vertex_attributes.GetPhysicalBaseAddress(); // GPUREG_ATTR_BUF_BASE | ||||||
| @@ -346,7 +344,7 @@ void RasterizerVulkan::SetupVertexArray(u32 vs_input_size, u32 vs_input_index_mi | |||||||
|     std::array<bool, 16> enable_attributes{}; |     std::array<bool, 16> enable_attributes{}; | ||||||
|     std::array<u64, 16> binding_offsets{}; |     std::array<u64, 16> binding_offsets{}; | ||||||
|  |  | ||||||
|     u32 buffer_offset = 0; |     u32 buffer_offset = array_offset; | ||||||
|     for (const auto& loader : vertex_attributes.attribute_loaders) { |     for (const auto& loader : vertex_attributes.attribute_loaders) { | ||||||
|         if (loader.component_count == 0 || loader.byte_count == 0) { |         if (loader.component_count == 0 || loader.byte_count == 0) { | ||||||
|             continue; |             continue; | ||||||
| @@ -401,13 +399,14 @@ void RasterizerVulkan::SetupVertexArray(u32 vs_input_size, u32 vs_input_index_mi | |||||||
|         binding.stride.Assign(loader.byte_count); |         binding.stride.Assign(loader.byte_count); | ||||||
|  |  | ||||||
|         // Keep track of the binding offsets so we can bind the vertex buffer later |         // Keep track of the binding offsets so we can bind the vertex buffer later | ||||||
|         binding_offsets[layout.binding_count++] = array_offset + buffer_offset; |         binding_offsets[layout.binding_count++] = buffer_offset; | ||||||
|         array_ptr += data_size; |         array_ptr += data_size; | ||||||
|         buffer_offset += data_size; |         buffer_offset += data_size; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Reserve the last binding for fixed attributes |     // Reserve the last binding for fixed attributes | ||||||
|     u32 offset = 0; |     u32 offset = 0; | ||||||
|  |     bool has_fixed_binding = false; | ||||||
|     for (std::size_t i = 0; i < 16; i++) { |     for (std::size_t i = 0; i < 16; i++) { | ||||||
|         if (vertex_attributes.IsDefaultAttribute(i)) { |         if (vertex_attributes.IsDefaultAttribute(i)) { | ||||||
|             const u32 reg = regs.vs.GetRegisterForAttribute(i); |             const u32 reg = regs.vs.GetRegisterForAttribute(i); | ||||||
| @@ -420,12 +419,6 @@ void RasterizerVulkan::SetupVertexArray(u32 vs_input_size, u32 vs_input_index_mi | |||||||
|                 const u32 data_size = sizeof(float) * static_cast<u32>(data.size()); |                 const u32 data_size = sizeof(float) * static_cast<u32>(data.size()); | ||||||
|                 std::memcpy(array_ptr, data.data(), data_size); |                 std::memcpy(array_ptr, data.data(), data_size); | ||||||
|  |  | ||||||
|                 // Define the binding. Note that the counter is not incremented |  | ||||||
|                 VertexBinding& binding = layout.bindings.at(layout.binding_count); |  | ||||||
|                 binding.binding.Assign(layout.binding_count); |  | ||||||
|                 binding.fixed.Assign(1); |  | ||||||
|                 binding.stride.Assign(offset); |  | ||||||
|  |  | ||||||
|                 VertexAttribute& attribute = layout.attributes.at(layout.attribute_count++); |                 VertexAttribute& attribute = layout.attributes.at(layout.attribute_count++); | ||||||
|                 attribute.binding.Assign(layout.binding_count); |                 attribute.binding.Assign(layout.binding_count); | ||||||
|                 attribute.location.Assign(reg); |                 attribute.location.Assign(reg); | ||||||
| @@ -435,18 +428,28 @@ void RasterizerVulkan::SetupVertexArray(u32 vs_input_size, u32 vs_input_index_mi | |||||||
|  |  | ||||||
|                 offset += data_size; |                 offset += data_size; | ||||||
|                 array_ptr += data_size; |                 array_ptr += data_size; | ||||||
|                 binding_offsets[layout.binding_count] = array_offset + buffer_offset; |                 has_fixed_binding = true; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (has_fixed_binding) { | ||||||
|  |         VertexBinding& binding = layout.bindings.at(layout.binding_count); | ||||||
|  |         binding.binding.Assign(layout.binding_count); | ||||||
|  |         binding.fixed.Assign(1); | ||||||
|  |         binding.stride.Assign(offset); | ||||||
|  |  | ||||||
|  |         binding_offsets[layout.binding_count++] = buffer_offset; | ||||||
|  |         buffer_offset += offset; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     pipeline_info.vertex_layout = layout; |     pipeline_info.vertex_layout = layout; | ||||||
|     vertex_buffer.Commit(vs_input_size); |     vertex_buffer.Commit(buffer_offset - array_offset); | ||||||
|  |  | ||||||
|     std::array<vk::Buffer, 16> buffers; |     std::array<vk::Buffer, 16> buffers; | ||||||
|     buffers.fill(vertex_buffer.GetHandle()); |     buffers.fill(vertex_buffer.GetHandle()); | ||||||
|  |  | ||||||
|     // Bind the vertex buffers with all the bindings |     // Bind the vertex buffer with all the bindings | ||||||
|     vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); |     vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); | ||||||
|     command_buffer.bindVertexBuffers(0, layout.binding_count, buffers.data(), |     command_buffer.bindVertexBuffers(0, layout.binding_count, buffers.data(), | ||||||
|                                      binding_offsets.data()); |                                      binding_offsets.data()); | ||||||
| @@ -503,6 +506,7 @@ bool RasterizerVulkan::AccelerateDrawBatchInternal(bool is_indexed) { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     SetupVertexArray(vs_input_size, vs_input_index_min, vs_input_index_max); |     SetupVertexArray(vs_input_size, vs_input_index_min, vs_input_index_max); | ||||||
|  |     pipeline_info.rasterization.topology.Assign(regs.pipeline.triangle_topology); | ||||||
|     pipeline_cache.BindPipeline(pipeline_info); |     pipeline_cache.BindPipeline(pipeline_info); | ||||||
|  |  | ||||||
|     vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); |     vk::CommandBuffer command_buffer = scheduler.GetRenderCommandBuffer(); | ||||||
| @@ -528,7 +532,7 @@ bool RasterizerVulkan::AccelerateDrawBatchInternal(bool is_indexed) { | |||||||
|         command_buffer.bindIndexBuffer(index_buffer.GetHandle(), index_offset, index_type); |         command_buffer.bindIndexBuffer(index_buffer.GetHandle(), index_offset, index_type); | ||||||
|  |  | ||||||
|         // Submit draw |         // Submit draw | ||||||
|         command_buffer.drawIndexed(regs.pipeline.num_vertices, 1, 0, 0, 0); |         command_buffer.drawIndexed(regs.pipeline.num_vertices, 1, 0, -vs_input_index_min, 0); | ||||||
|     } else { |     } else { | ||||||
|         command_buffer.draw(regs.pipeline.num_vertices, 1, 0, 0); |         command_buffer.draw(regs.pipeline.num_vertices, 1, 0, 0); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ | |||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
| #include "video_core/pica_state.h" | #include "video_core/pica_state.h" | ||||||
|  | #include "video_core/renderer_opengl/gl_shader_decompiler.h" | ||||||
| #include "video_core/renderer_vulkan/vk_shader_gen.h" | #include "video_core/renderer_vulkan/vk_shader_gen.h" | ||||||
| #include "video_core/video_core.h" | #include "video_core/video_core.h" | ||||||
|  |  | ||||||
| @@ -1586,8 +1587,8 @@ void main() { | |||||||
|  |  | ||||||
| std::optional<std::string> GenerateVertexShader(const Pica::Shader::ShaderSetup& setup, | std::optional<std::string> GenerateVertexShader(const Pica::Shader::ShaderSetup& setup, | ||||||
|                                                 const PicaVSConfig& config) { |                                                 const PicaVSConfig& config) { | ||||||
|     /*std::string out = "#extension GL_ARB_separate_shader_objects : enable\n"; |     std::string out = "#extension GL_ARB_separate_shader_objects : enable\n"; | ||||||
|     out += ShaderDecompiler::GetCommonDeclarations(); |     out += OpenGL::ShaderDecompiler::GetCommonDeclarations(); | ||||||
|  |  | ||||||
|     std::array<bool, 16> used_regs{}; |     std::array<bool, 16> used_regs{}; | ||||||
|     const auto get_input_reg = [&used_regs](u32 reg) { |     const auto get_input_reg = [&used_regs](u32 reg) { | ||||||
| @@ -1604,18 +1605,19 @@ std::optional<std::string> GenerateVertexShader(const Pica::Shader::ShaderSetup& | |||||||
|         return ""; |         return ""; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     auto program_source_opt = ShaderDecompiler::DecompileProgram( |     auto program_source_opt = OpenGL::ShaderDecompiler::DecompileProgram( | ||||||
|         setup.program_code, setup.swizzle_data, config.state.main_offset, get_input_reg, |         setup.program_code, setup.swizzle_data, config.state.main_offset, get_input_reg, | ||||||
|         get_output_reg, config.state.sanitize_mul); |         get_output_reg, config.state.sanitize_mul); | ||||||
|  |  | ||||||
|     if (!program_source_opt) |     if (!program_source_opt) { | ||||||
|         return std::nullopt; |         return std::nullopt; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     std::string& program_source = program_source_opt->code; |     std::string& program_source = program_source_opt->code; | ||||||
|  |  | ||||||
|     out += R"( |     out += R"( | ||||||
| #define uniforms vs_uniforms | #define uniforms vs_uniforms | ||||||
| layout (std140) uniform vs_config { | layout (set = 0, binding = 0, std140) uniform vs_config { | ||||||
|     pica_uniforms uniforms; |     pica_uniforms uniforms; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -1623,15 +1625,14 @@ layout (std140) uniform vs_config { | |||||||
|     // input attributes declaration |     // input attributes declaration | ||||||
|     for (std::size_t i = 0; i < used_regs.size(); ++i) { |     for (std::size_t i = 0; i < used_regs.size(); ++i) { | ||||||
|         if (used_regs[i]) { |         if (used_regs[i]) { | ||||||
|             out += fmt::format("layout(location = {0}) in vec4 vs_in_reg{0};\n", i); |             out += fmt::format("layout(location = {0}) in {1}vec4 vs_in_reg{0};\n", i, i == 3 ? "" : ""); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     out += '\n'; |     out += '\n'; | ||||||
|  |  | ||||||
|     // output attributes declaration |     // output attributes declaration | ||||||
|     for (u32 i = 0; i < config.state.num_outputs; ++i) { |     for (u32 i = 0; i < config.state.num_outputs; ++i) { | ||||||
|         out += "layout(location = " + std::to_string(i) + ") out vec4 vs_out_attr" + |         out += fmt::format("layout(location = {}) out vec4 vs_out_attr{};\n", i, i); | ||||||
| std::to_string(i) + ";\n"; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     out += "\nvoid main() {\n"; |     out += "\nvoid main() {\n"; | ||||||
| @@ -1642,19 +1643,17 @@ std::to_string(i) + ";\n"; | |||||||
|  |  | ||||||
|     out += program_source; |     out += program_source; | ||||||
|  |  | ||||||
|     return {{std::move(out)}};*/ |     return out; | ||||||
|     return std::nullopt; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| static std::string GetGSCommonSource(const PicaGSConfigCommonRaw& config) { | static std::string GetGSCommonSource(const PicaGSConfigCommonRaw& config) { | ||||||
|     /*std::string out = GetVertexInterfaceDeclaration(true, separable_shader); |     std::string out = GetVertexInterfaceDeclaration(true); | ||||||
|     out += UniformBlockDef; |     out += UniformBlockDef; | ||||||
|     out += ShaderDecompiler::GetCommonDeclarations(); |     out += OpenGL::ShaderDecompiler::GetCommonDeclarations(); | ||||||
|  |  | ||||||
|     out += '\n'; |     out += '\n'; | ||||||
|     for (u32 i = 0; i < config.vs_output_attributes; ++i) { |     for (u32 i = 0; i < config.vs_output_attributes; ++i) { | ||||||
|         out += ("layout(location = " + std::to_string(i) + ") in vec4 vs_out_attr" + |         out += fmt::format("layout(location = {}) in vec4 vs_out_attr{}[];\n", i, i); | ||||||
| std::to_string(i) + "[];\n"; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     out += R"( |     out += R"( | ||||||
| @@ -1728,8 +1727,7 @@ void EmitPrim(Vertex vtx0, Vertex vtx1, Vertex vtx2) { | |||||||
| } | } | ||||||
| )"; | )"; | ||||||
|  |  | ||||||
|     return out;*/ |     return out; | ||||||
|     return ""; |  | ||||||
| }; | }; | ||||||
|  |  | ||||||
| std::string GenerateFixedGeometryShader(const PicaFixedGSConfig& config) { | std::string GenerateFixedGeometryShader(const PicaFixedGSConfig& config) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user