diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index d7e27695e..87943876e 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -90,8 +90,17 @@ vk::Format ToVkAttributeFormat(VertexAttribute attrib) { case 4: 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: - LOG_CRITICAL(Render_Vulkan, "Unimplemented vertex attribute format!"); + LOG_CRITICAL(Render_Vulkan, "Unimplemented vertex attribute type {}", attrib.type.Value()); UNREACHABLE(); } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 630123a19..c59bdd5fc 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -330,14 +330,12 @@ void RasterizerVulkan::SetupVertexArray(u32 vs_input_size, u32 vs_input_index_mi u32 vs_input_index_max) { 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 - * 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 - * 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 - * or interleave them in the same loader. - */ + // 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 + // 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 + // as something analogous to Vulkan bindings. The user can store attributes in separate loaders + // or interleave them in the same loader. const auto& regs = Pica::g_state.regs; const auto& vertex_attributes = regs.pipeline.vertex_attributes; 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 enable_attributes{}; std::array binding_offsets{}; - u32 buffer_offset = 0; + u32 buffer_offset = array_offset; for (const auto& loader : vertex_attributes.attribute_loaders) { if (loader.component_count == 0 || loader.byte_count == 0) { continue; @@ -401,13 +399,14 @@ void RasterizerVulkan::SetupVertexArray(u32 vs_input_size, u32 vs_input_index_mi binding.stride.Assign(loader.byte_count); // 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; buffer_offset += data_size; } // Reserve the last binding for fixed attributes u32 offset = 0; + bool has_fixed_binding = false; for (std::size_t i = 0; i < 16; i++) { if (vertex_attributes.IsDefaultAttribute(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(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++); attribute.binding.Assign(layout.binding_count); attribute.location.Assign(reg); @@ -435,18 +428,28 @@ void RasterizerVulkan::SetupVertexArray(u32 vs_input_size, u32 vs_input_index_mi offset += 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; - vertex_buffer.Commit(vs_input_size); + vertex_buffer.Commit(buffer_offset - array_offset); std::array buffers; 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(); command_buffer.bindVertexBuffers(0, layout.binding_count, buffers.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); + pipeline_info.rasterization.topology.Assign(regs.pipeline.triangle_topology); pipeline_cache.BindPipeline(pipeline_info); 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); // 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 { command_buffer.draw(regs.pipeline.num_vertices, 1, 0, 0); } diff --git a/src/video_core/renderer_vulkan/vk_shader_gen.cpp b/src/video_core/renderer_vulkan/vk_shader_gen.cpp index 17e55a766..3550ec85e 100644 --- a/src/video_core/renderer_vulkan/vk_shader_gen.cpp +++ b/src/video_core/renderer_vulkan/vk_shader_gen.cpp @@ -8,6 +8,7 @@ #include "common/logging/log.h" #include "core/core.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/video_core.h" @@ -1586,8 +1587,8 @@ void main() { std::optional GenerateVertexShader(const Pica::Shader::ShaderSetup& setup, const PicaVSConfig& config) { - /*std::string out = "#extension GL_ARB_separate_shader_objects : enable\n"; - out += ShaderDecompiler::GetCommonDeclarations(); + std::string out = "#extension GL_ARB_separate_shader_objects : enable\n"; + out += OpenGL::ShaderDecompiler::GetCommonDeclarations(); std::array used_regs{}; const auto get_input_reg = [&used_regs](u32 reg) { @@ -1604,18 +1605,19 @@ std::optional GenerateVertexShader(const Pica::Shader::ShaderSetup& 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, get_output_reg, config.state.sanitize_mul); - if (!program_source_opt) + if (!program_source_opt) { return std::nullopt; + } std::string& program_source = program_source_opt->code; out += R"( #define uniforms vs_uniforms -layout (std140) uniform vs_config { +layout (set = 0, binding = 0, std140) uniform vs_config { pica_uniforms uniforms; }; @@ -1623,15 +1625,14 @@ layout (std140) uniform vs_config { // input attributes declaration for (std::size_t i = 0; i < used_regs.size(); ++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'; // output attributes declaration for (u32 i = 0; i < config.state.num_outputs; ++i) { - out += "layout(location = " + std::to_string(i) + ") out vec4 vs_out_attr" + -std::to_string(i) + ";\n"; + out += fmt::format("layout(location = {}) out vec4 vs_out_attr{};\n", i, i); } out += "\nvoid main() {\n"; @@ -1642,19 +1643,17 @@ std::to_string(i) + ";\n"; out += program_source; - return {{std::move(out)}};*/ - return std::nullopt; + return out; } static std::string GetGSCommonSource(const PicaGSConfigCommonRaw& config) { - /*std::string out = GetVertexInterfaceDeclaration(true, separable_shader); + std::string out = GetVertexInterfaceDeclaration(true); out += UniformBlockDef; - out += ShaderDecompiler::GetCommonDeclarations(); + out += OpenGL::ShaderDecompiler::GetCommonDeclarations(); out += '\n'; for (u32 i = 0; i < config.vs_output_attributes; ++i) { - out += ("layout(location = " + std::to_string(i) + ") in vec4 vs_out_attr" + -std::to_string(i) + "[];\n"; + out += fmt::format("layout(location = {}) in vec4 vs_out_attr{}[];\n", i, i); } out += R"( @@ -1728,8 +1727,7 @@ void EmitPrim(Vertex vtx0, Vertex vtx1, Vertex vtx2) { } )"; - return out;*/ - return ""; + return out; }; std::string GenerateFixedGeometryShader(const PicaFixedGSConfig& config) {