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