summaryrefslogtreecommitdiffstats
path: root/src/video_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core')
-rw-r--r--src/video_core/CMakeLists.txt54
-rw-r--r--src/video_core/command_processor.cpp123
-rw-r--r--src/video_core/debug_utils/debug_utils.cpp301
-rw-r--r--src/video_core/debug_utils/debug_utils.h43
-rw-r--r--src/video_core/pica.cpp487
-rw-r--r--src/video_core/pica.h1405
-rw-r--r--src/video_core/pica_state.h6
-rw-r--r--src/video_core/primitive_assembly.cpp20
-rw-r--r--src/video_core/primitive_assembly.h14
-rw-r--r--src/video_core/rasterizer.h20
-rw-r--r--src/video_core/regs.cpp488
-rw-r--r--src/video_core/regs.h142
-rw-r--r--src/video_core/regs_framebuffer.h284
-rw-r--r--src/video_core/regs_lighting.h294
-rw-r--r--src/video_core/regs_pipeline.h230
-rw-r--r--src/video_core/regs_rasterizer.h129
-rw-r--r--src/video_core/regs_shader.h104
-rw-r--r--src/video_core/regs_texturing.h328
-rw-r--r--src/video_core/renderer_base.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp349
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h56
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp32
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h13
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.cpp105
-rw-r--r--src/video_core/renderer_opengl/pica_to_gl.h20
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp17
-rw-r--r--src/video_core/shader/shader.cpp65
-rw-r--r--src/video_core/shader/shader.h65
-rw-r--r--src/video_core/shader/shader_interpreter.cpp6
-rw-r--r--src/video_core/shader/shader_interpreter.h8
-rw-r--r--src/video_core/shader/shader_jit_x64_compiler.cpp17
-rw-r--r--src/video_core/shader/shader_jit_x64_compiler.h4
-rw-r--r--src/video_core/swrasterizer/clipper.cpp (renamed from src/video_core/clipper.cpp)37
-rw-r--r--src/video_core/swrasterizer/clipper.h (renamed from src/video_core/clipper.h)0
-rw-r--r--src/video_core/swrasterizer/framebuffer.cpp358
-rw-r--r--src/video_core/swrasterizer/framebuffer.h29
-rw-r--r--src/video_core/swrasterizer/rasterizer.cpp (renamed from src/video_core/rasterizer.cpp)732
-rw-r--r--src/video_core/swrasterizer/rasterizer.h48
-rw-r--r--src/video_core/swrasterizer/swrasterizer.cpp (renamed from src/video_core/swrasterizer.cpp)4
-rw-r--r--src/video_core/swrasterizer/swrasterizer.h (renamed from src/video_core/swrasterizer.h)0
-rw-r--r--src/video_core/swrasterizer/texturing.cpp228
-rw-r--r--src/video_core/swrasterizer/texturing.h28
-rw-r--r--src/video_core/texture/etc1.cpp122
-rw-r--r--src/video_core/texture/etc1.h16
-rw-r--r--src/video_core/texture/texture_decode.cpp227
-rw-r--r--src/video_core/texture/texture_decode.h60
-rw-r--r--src/video_core/vertex_loader.cpp22
-rw-r--r--src/video_core/vertex_loader.h12
48 files changed, 3809 insertions, 3345 deletions
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index d55b84ce0..5317719e8 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -1,26 +1,46 @@
set(SRCS
+ command_processor.cpp
+ debug_utils/debug_utils.cpp
+ pica.cpp
+ primitive_assembly.cpp
+ regs.cpp
+ renderer_base.cpp
renderer_opengl/gl_rasterizer.cpp
renderer_opengl/gl_rasterizer_cache.cpp
renderer_opengl/gl_shader_gen.cpp
renderer_opengl/gl_shader_util.cpp
renderer_opengl/gl_state.cpp
renderer_opengl/renderer_opengl.cpp
- debug_utils/debug_utils.cpp
- clipper.cpp
- command_processor.cpp
- pica.cpp
- primitive_assembly.cpp
- rasterizer.cpp
- renderer_base.cpp
shader/shader.cpp
shader/shader_interpreter.cpp
- swrasterizer.cpp
+ swrasterizer/clipper.cpp
+ swrasterizer/framebuffer.cpp
+ swrasterizer/rasterizer.cpp
+ swrasterizer/swrasterizer.cpp
+ swrasterizer/texturing.cpp
+ texture/etc1.cpp
+ texture/texture_decode.cpp
vertex_loader.cpp
video_core.cpp
)
set(HEADERS
+ command_processor.h
debug_utils/debug_utils.h
+ gpu_debugger.h
+ pica.h
+ pica_state.h
+ pica_types.h
+ primitive_assembly.h
+ rasterizer_interface.h
+ regs.h
+ regs_framebuffer.h
+ regs_lighting.h
+ regs_pipeline.h
+ regs_rasterizer.h
+ regs_shader.h
+ regs_texturing.h
+ renderer_base.h
renderer_opengl/gl_rasterizer.h
renderer_opengl/gl_rasterizer_cache.h
renderer_opengl/gl_resource_manager.h
@@ -29,20 +49,16 @@ set(HEADERS
renderer_opengl/gl_state.h
renderer_opengl/pica_to_gl.h
renderer_opengl/renderer_opengl.h
- clipper.h
- command_processor.h
- gpu_debugger.h
- pica.h
- pica_state.h
- pica_types.h
- primitive_assembly.h
- rasterizer.h
- rasterizer_interface.h
- renderer_base.h
shader/debug_data.h
shader/shader.h
shader/shader_interpreter.h
- swrasterizer.h
+ swrasterizer/clipper.h
+ swrasterizer/framebuffer.h
+ swrasterizer/rasterizer.h
+ swrasterizer/swrasterizer.h
+ swrasterizer/texturing.h
+ texture/etc1.h
+ texture/texture_decode.h
utils.h
vertex_loader.h
video_core.h
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp
index eb79974a8..2e32ff905 100644
--- a/src/video_core/command_processor.cpp
+++ b/src/video_core/command_processor.cpp
@@ -16,11 +16,13 @@
#include "core/tracer/recorder.h"
#include "video_core/command_processor.h"
#include "video_core/debug_utils/debug_utils.h"
-#include "video_core/pica.h"
#include "video_core/pica_state.h"
#include "video_core/pica_types.h"
#include "video_core/primitive_assembly.h"
#include "video_core/rasterizer_interface.h"
+#include "video_core/regs.h"
+#include "video_core/regs_pipeline.h"
+#include "video_core/regs_texturing.h"
#include "video_core/renderer_base.h"
#include "video_core/shader/shader.h"
#include "video_core/vertex_loader.h"
@@ -49,19 +51,23 @@ MICROPROFILE_DEFINE(GPU_Drawing, "GPU", "Drawing", MP_RGB(50, 50, 240));
static void WritePicaReg(u32 id, u32 value, u32 mask) {
auto& regs = g_state.regs;
- if (id >= regs.NumIds())
+ if (id >= Regs::NUM_REGS) {
+ LOG_ERROR(HW_GPU,
+ "Commandlist tried to write to invalid register 0x%03X (value: %08X, mask: %X)",
+ id, value, mask);
return;
+ }
// TODO: Figure out how register masking acts on e.g. vs.uniform_setup.set_value
- u32 old_value = regs[id];
+ u32 old_value = regs.reg_array[id];
const u32 write_mask = expand_bits_to_bytes[mask];
- regs[id] = (old_value & ~write_mask) | (value & write_mask);
+ regs.reg_array[id] = (old_value & ~write_mask) | (value & write_mask);
// Double check for is_pica_tracing to avoid call overhead
if (DebugUtils::IsPicaTracing()) {
- DebugUtils::OnPicaRegWrite({(u16)id, (u16)mask, regs[id]});
+ DebugUtils::OnPicaRegWrite({(u16)id, (u16)mask, regs.reg_array[id]});
}
if (g_debug_context)
@@ -74,23 +80,23 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
Service::GSP::SignalInterrupt(Service::GSP::InterruptId::P3D);
break;
- case PICA_REG_INDEX_WORKAROUND(triangle_topology, 0x25E):
- g_state.primitive_assembler.Reconfigure(regs.triangle_topology);
+ case PICA_REG_INDEX(pipeline.triangle_topology):
+ g_state.primitive_assembler.Reconfigure(regs.pipeline.triangle_topology);
break;
- case PICA_REG_INDEX_WORKAROUND(restart_primitive, 0x25F):
+ case PICA_REG_INDEX(pipeline.restart_primitive):
g_state.primitive_assembler.Reset();
break;
- case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.index, 0x232):
+ case PICA_REG_INDEX(pipeline.vs_default_attributes_setup.index):
g_state.immediate.current_attribute = 0;
default_attr_counter = 0;
break;
// Load default vertex input attributes
- case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[0], 0x233):
- case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[1], 0x234):
- case PICA_REG_INDEX_WORKAROUND(vs_default_attributes_setup.set_value[2], 0x235): {
+ case PICA_REG_INDEX_WORKAROUND(pipeline.vs_default_attributes_setup.set_value[0], 0x233):
+ case PICA_REG_INDEX_WORKAROUND(pipeline.vs_default_attributes_setup.set_value[1], 0x234):
+ case PICA_REG_INDEX_WORKAROUND(pipeline.vs_default_attributes_setup.set_value[2], 0x235): {
// TODO: Does actual hardware indeed keep an intermediate buffer or does
// it directly write the values?
default_attr_write_buffer[default_attr_counter++] = value;
@@ -102,7 +108,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
if (default_attr_counter >= 3) {
default_attr_counter = 0;
- auto& setup = regs.vs_default_attributes_setup;
+ auto& setup = regs.pipeline.vs_default_attributes_setup;
if (setup.index >= 16) {
LOG_ERROR(HW_GPU, "Invalid VS default attribute index %d", (int)setup.index);
@@ -125,20 +131,21 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
// TODO: Verify that this actually modifies the register!
if (setup.index < 15) {
- g_state.vs_default_attributes[setup.index] = attribute;
+ g_state.input_default_attributes.attr[setup.index] = attribute;
setup.index++;
} else {
- // Put each attribute into an immediate input buffer.
- // When all specified immediate attributes are present, the Vertex Shader is invoked
- // and everything is
- // sent to the primitive assembler.
+ // Put each attribute into an immediate input buffer. When all specified immediate
+ // attributes are present, the Vertex Shader is invoked and everything is sent to
+ // the primitive assembler.
auto& immediate_input = g_state.immediate.input_vertex;
auto& immediate_attribute_id = g_state.immediate.current_attribute;
- immediate_input.attr[immediate_attribute_id++] = attribute;
+ immediate_input.attr[immediate_attribute_id] = attribute;
- if (immediate_attribute_id >= regs.vs.num_input_attributes + 1) {
+ if (immediate_attribute_id < regs.pipeline.max_input_attrib_index) {
+ immediate_attribute_id += 1;
+ } else {
MICROPROFILE_SCOPE(GPU_Drawing);
immediate_attribute_id = 0;
@@ -150,10 +157,11 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
g_debug_context->OnEvent(DebugContext::Event::VertexShaderInvocation,
static_cast<void*>(&immediate_input));
Shader::UnitState shader_unit;
- shader_unit.LoadInputVertex(immediate_input, regs.vs.num_input_attributes + 1);
+ Shader::AttributeBuffer output{};
+
+ shader_unit.LoadInput(regs.vs, immediate_input);
shader_engine->Run(g_state.vs, shader_unit);
- auto output_vertex = Shader::OutputVertex::FromRegisters(
- shader_unit.registers.output, regs, regs.vs.output_mask);
+ shader_unit.WriteOutput(regs.vs, output);
// Send to renderer
using Pica::Shader::OutputVertex;
@@ -162,15 +170,17 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
VideoCore::g_renderer->Rasterizer()->AddTriangle(v0, v1, v2);
};
- g_state.primitive_assembler.SubmitVertex(output_vertex, AddTriangle);
+ g_state.primitive_assembler.SubmitVertex(
+ Shader::OutputVertex::FromAttributeBuffer(regs.rasterizer, output),
+ AddTriangle);
}
}
}
break;
}
- case PICA_REG_INDEX(gpu_mode):
- if (regs.gpu_mode == Regs::GPUMode::Configuring) {
+ case PICA_REG_INDEX(pipeline.gpu_mode):
+ if (regs.pipeline.gpu_mode == PipelineRegs::GPUMode::Configuring) {
MICROPROFILE_SCOPE(GPU_Drawing);
// Draw immediate mode triangles when GPU Mode is set to GPUMode::Configuring
@@ -182,19 +192,20 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
}
break;
- case PICA_REG_INDEX_WORKAROUND(command_buffer.trigger[0], 0x23c):
- case PICA_REG_INDEX_WORKAROUND(command_buffer.trigger[1], 0x23d): {
- unsigned index = static_cast<unsigned>(id - PICA_REG_INDEX(command_buffer.trigger[0]));
- u32* head_ptr =
- (u32*)Memory::GetPhysicalPointer(regs.command_buffer.GetPhysicalAddress(index));
+ case PICA_REG_INDEX_WORKAROUND(pipeline.command_buffer.trigger[0], 0x23c):
+ case PICA_REG_INDEX_WORKAROUND(pipeline.command_buffer.trigger[1], 0x23d): {
+ unsigned index =
+ static_cast<unsigned>(id - PICA_REG_INDEX(pipeline.command_buffer.trigger[0]));
+ u32* head_ptr = (u32*)Memory::GetPhysicalPointer(
+ regs.pipeline.command_buffer.GetPhysicalAddress(index));
g_state.cmd_list.head_ptr = g_state.cmd_list.current_ptr = head_ptr;
- g_state.cmd_list.length = regs.command_buffer.GetSize(index) / sizeof(u32);
+ g_state.cmd_list.length = regs.pipeline.command_buffer.GetSize(index) / sizeof(u32);
break;
}
// It seems like these trigger vertex rendering
- case PICA_REG_INDEX(trigger_draw):
- case PICA_REG_INDEX(trigger_draw_indexed): {
+ case PICA_REG_INDEX(pipeline.trigger_draw):
+ case PICA_REG_INDEX(pipeline.trigger_draw_indexed): {
MICROPROFILE_SCOPE(GPU_Drawing);
#if PICA_LOG_TEV
@@ -206,13 +217,13 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
// Processes information about internal vertex attributes to figure out how a vertex is
// loaded.
// Later, these can be compiled and cached.
- const u32 base_address = regs.vertex_attributes.GetPhysicalBaseAddress();
- VertexLoader loader(regs);
+ const u32 base_address = regs.pipeline.vertex_attributes.GetPhysicalBaseAddress();
+ VertexLoader loader(regs.pipeline);
// Load vertices
- bool is_indexed = (id == PICA_REG_INDEX(trigger_draw_indexed));
+ bool is_indexed = (id == PICA_REG_INDEX(pipeline.trigger_draw_indexed));
- const auto& index_info = regs.index_array;
+ const auto& index_info = regs.pipeline.index_array;
const u8* index_address_8 = Memory::GetPhysicalPointer(base_address + index_info.offset);
const u16* index_address_16 = reinterpret_cast<const u16*>(index_address_8);
bool index_u16 = index_info.format != 0;
@@ -221,13 +232,13 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
if (g_debug_context && g_debug_context->recorder) {
for (int i = 0; i < 3; ++i) {
- const auto texture = regs.GetTextures()[i];
+ const auto texture = regs.texturing.GetTextures()[i];
if (!texture.enabled)
continue;
u8* texture_data = Memory::GetPhysicalPointer(texture.config.GetPhysicalAddress());
g_debug_context->recorder->MemoryAccessed(
- texture_data, Pica::Regs::NibblesPerPixel(texture.format) *
+ texture_data, Pica::TexturingRegs::NibblesPerPixel(texture.format) *
texture.config.width / 2 * texture.config.height,
texture.config.GetPhysicalAddress());
}
@@ -250,11 +261,11 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
shader_engine->SetupBatch(g_state.vs, regs.vs.main_offset);
- for (unsigned int index = 0; index < regs.num_vertices; ++index) {
+ for (unsigned int index = 0; index < regs.pipeline.num_vertices; ++index) {
// Indexed rendering doesn't use the start offset
unsigned int vertex =
is_indexed ? (index_u16 ? index_address_16[index] : index_address_8[index])
- : (index + regs.vertex_offset);
+ : (index + regs.pipeline.vertex_offset);
// -1 is a common special value used for primitive restart. Since it's unknown if
// the PICA supports it, and it would mess up the caching, guard against it here.
@@ -280,19 +291,19 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
if (!vertex_cache_hit) {
// Initialize data for the current vertex
- Shader::InputVertex input;
+ Shader::AttributeBuffer input, output{};
loader.LoadVertex(base_address, index, vertex, input, memory_accesses);
// Send to vertex shader
if (g_debug_context)
g_debug_context->OnEvent(DebugContext::Event::VertexShaderInvocation,
(void*)&input);
- shader_unit.LoadInputVertex(input, loader.GetNumTotalAttributes());
+ shader_unit.LoadInput(regs.vs, input);
shader_engine->Run(g_state.vs, shader_unit);
+ shader_unit.WriteOutput(regs.vs, output);
// Retrieve vertex from register data
- output_vertex = Shader::OutputVertex::FromRegisters(shader_unit.registers.output,
- regs, regs.vs.output_mask);
+ output_vertex = Shader::OutputVertex::FromAttributeBuffer(regs.rasterizer, output);
if (is_indexed) {
vertex_cache[vertex_cache_pos] = output_vertex;
@@ -434,16 +445,16 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) {
break;
}
- case PICA_REG_INDEX_WORKAROUND(fog_lut_data[0], 0xe8):
- case PICA_REG_INDEX_WORKAROUND(fog_lut_data[1], 0xe9):
- case PICA_REG_INDEX_WORKAROUND(fog_lut_data[2], 0xea):
- case PICA_REG_INDEX_WORKAROUND(fog_lut_data[3], 0xeb):
- case PICA_REG_INDEX_WORKAROUND(fog_lut_data[4], 0xec):
- case PICA_REG_INDEX_WORKAROUND(fog_lut_data[5], 0xed):
- case PICA_REG_INDEX_WORKAROUND(fog_lut_data[6], 0xee):
- case PICA_REG_INDEX_WORKAROUND(fog_lut_data[7], 0xef): {
- g_state.fog.lut[regs.fog_lut_offset % 128].raw = value;
- regs.fog_lut_offset.Assign(regs.fog_lut_offset + 1);
+ case PICA_REG_INDEX_WORKAROUND(texturing.fog_lut_data[0], 0xe8):
+ case PICA_REG_INDEX_WORKAROUND(texturing.fog_lut_data[1], 0xe9):
+ case PICA_REG_INDEX_WORKAROUND(texturing.fog_lut_data[2], 0xea):
+ case PICA_REG_INDEX_WORKAROUND(texturing.fog_lut_data[3], 0xeb):
+ case PICA_REG_INDEX_WORKAROUND(texturing.fog_lut_data[4], 0xec):
+ case PICA_REG_INDEX_WORKAROUND(texturing.fog_lut_data[5], 0xed):
+ case PICA_REG_INDEX_WORKAROUND(texturing.fog_lut_data[6], 0xee):
+ case PICA_REG_INDEX_WORKAROUND(texturing.fog_lut_data[7], 0xef): {
+ g_state.fog.lut[regs.texturing.fog_lut_offset % 128].raw = value;
+ regs.texturing.fog_lut_offset.Assign(regs.texturing.fog_lut_offset + 1);
break;
}
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp
index c44b3d95a..47dbc8cc8 100644
--- a/src/video_core/debug_utils/debug_utils.cpp
+++ b/src/video_core/debug_utils/debug_utils.cpp
@@ -29,12 +29,15 @@
#include "common/math_util.h"
#include "common/vector_math.h"
#include "video_core/debug_utils/debug_utils.h"
-#include "video_core/pica.h"
#include "video_core/pica_state.h"
#include "video_core/pica_types.h"
#include "video_core/rasterizer_interface.h"
+#include "video_core/regs_rasterizer.h"
+#include "video_core/regs_shader.h"
+#include "video_core/regs_texturing.h"
#include "video_core/renderer_base.h"
#include "video_core/shader/shader.h"
+#include "video_core/texture/texture_decode.h"
#include "video_core/utils.h"
#include "video_core/video_core.h"
@@ -87,9 +90,9 @@ std::shared_ptr<DebugContext> g_debug_context; // TODO: Get rid of this global
namespace DebugUtils {
-void DumpShader(const std::string& filename, const Regs::ShaderConfig& config,
+void DumpShader(const std::string& filename, const ShaderRegs& config,
const Shader::ShaderSetup& setup,
- const Regs::VSOutputAttributes* output_attributes) {
+ const RasterizerRegs::VSOutputAttributes* output_attributes) {
struct StuffToWrite {
const u8* pointer;
u32 size;
@@ -128,7 +131,7 @@ void DumpShader(const std::string& filename, const Regs::ShaderConfig& config,
// This is put into a try-catch block to make sure we notice unknown configurations.
std::vector<OutputRegisterInfo> output_info_table;
for (unsigned i = 0; i < 7; ++i) {
- using OutputAttributes = Pica::Regs::VSOutputAttributes;
+ using OutputAttributes = Pica::RasterizerRegs::VSOutputAttributes;
// TODO: It's still unclear how the attribute components map to the register!
// Once we know that, this code probably will not make much sense anymore.
@@ -315,257 +318,6 @@ std::unique_ptr<PicaTrace> FinishPicaTracing() {
return ret;
}
-const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const TextureInfo& info,
- bool disable_alpha) {
- const unsigned int coarse_x = x & ~7;
- const unsigned int coarse_y = y & ~7;
-
- if (info.format != Regs::TextureFormat::ETC1 && info.format != Regs::TextureFormat::ETC1A4) {
- // TODO(neobrain): Fix code design to unify vertical block offsets!
- source += coarse_y * info.stride;
- }
-
- // TODO: Assert that width/height are multiples of block dimensions
-
- switch (info.format) {
- case Regs::TextureFormat::RGBA8: {
- auto res = Color::DecodeRGBA8(source + VideoCore::GetMortonOffset(x, y, 4));
- return {res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a())};
- }
-
- case Regs::TextureFormat::RGB8: {
- auto res = Color::DecodeRGB8(source + VideoCore::GetMortonOffset(x, y, 3));
- return {res.r(), res.g(), res.b(), 255};
- }
-
- case Regs::TextureFormat::RGB5A1: {
- auto res = Color::DecodeRGB5A1(source + VideoCore::GetMortonOffset(x, y, 2));
- return {res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a())};
- }
-
- case Regs::TextureFormat::RGB565: {
- auto res = Color::DecodeRGB565(source + VideoCore::GetMortonOffset(x, y, 2));
- return {res.r(), res.g(), res.b(), 255};
- }
-
- case Regs::TextureFormat::RGBA4: {
- auto res = Color::DecodeRGBA4(source + VideoCore::GetMortonOffset(x, y, 2));
- return {res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a())};
- }
-
- case Regs::TextureFormat::IA8: {
- const u8* source_ptr = source + VideoCore::GetMortonOffset(x, y, 2);
-
- if (disable_alpha) {
- // Show intensity as red, alpha as green
- return {source_ptr[1], source_ptr[0], 0, 255};
- } else {
- return {source_ptr[1], source_ptr[1], source_ptr[1], source_ptr[0]};
- }
- }
-
- case Regs::TextureFormat::RG8: {
- auto res = Color::DecodeRG8(source + VideoCore::GetMortonOffset(x, y, 2));
- return {res.r(), res.g(), 0, 255};
- }
-
- case Regs::TextureFormat::I8: {
- const u8* source_ptr = source + VideoCore::GetMortonOffset(x, y, 1);
- return {*source_ptr, *source_ptr, *source_ptr, 255};
- }
-
- case Regs::TextureFormat::A8: {
- const u8* source_ptr = source + VideoCore::GetMortonOffset(x, y, 1);
-
- if (disable_alpha) {
- return {*source_ptr, *source_ptr, *source_ptr, 255};
- } else {
- return {0, 0, 0, *source_ptr};
- }
- }
-
- case Regs::TextureFormat::IA4: {
- const u8* source_ptr = source + VideoCore::GetMortonOffset(x, y, 1);
-
- u8 i = Color::Convert4To8(((*source_ptr) & 0xF0) >> 4);
- u8 a = Color::Convert4To8((*source_ptr) & 0xF);
-
- if (disable_alpha) {
- // Show intensity as red, alpha as green
- return {i, a, 0, 255};
- } else {
- return {i, i, i, a};
- }
- }
-
- case Regs::TextureFormat::I4: {
- u32 morton_offset = VideoCore::GetMortonOffset(x, y, 1);
- const u8* source_ptr = source + morton_offset / 2;
-
- u8 i = (morton_offset % 2) ? ((*source_ptr & 0xF0) >> 4) : (*source_ptr & 0xF);
- i = Color::Convert4To8(i);
-
- return {i, i, i, 255};
- }
-
- case Regs::TextureFormat::A4: {
- u32 morton_offset = VideoCore::GetMortonOffset(x, y, 1);
- const u8* source_ptr = source + morton_offset / 2;
-
- u8 a = (morton_offset % 2) ? ((*source_ptr & 0xF0) >> 4) : (*source_ptr & 0xF);
- a = Color::Convert4To8(a);
-
- if (disable_alpha) {
- return {a, a, a, 255};
- } else {
- return {0, 0, 0, a};
- }
- }
-
- case Regs::TextureFormat::ETC1:
- case Regs::TextureFormat::ETC1A4: {
- bool has_alpha = (info.format == Regs::TextureFormat::ETC1A4);
-
- // ETC1 further subdivides each 8x8 tile into four 4x4 subtiles
- const int subtile_width = 4;
- const int subtile_height = 4;
-
- int subtile_index = ((x / subtile_width) & 1) + 2 * ((y / subtile_height) & 1);
- unsigned subtile_bytes = has_alpha ? 2 : 1; // TODO: Name...
-
- const u64* source_ptr = (const u64*)(source + coarse_x * subtile_bytes * 4 +
- coarse_y * subtile_bytes * 4 * (info.width / 8) +
- subtile_index * subtile_bytes * 8);
- u64 alpha = 0xFFFFFFFFFFFFFFFF;
- if (has_alpha) {
- alpha = *source_ptr;
- source_ptr++;
- }
-
- union ETC1Tile {
- // Each of these two is a collection of 16 bits (one per lookup value)
- BitField<0, 16, u64> table_subindexes;
- BitField<16, 16, u64> negation_flags;
-
- unsigned GetTableSubIndex(unsigned index) const {
- return (table_subindexes >> index) & 1;
- }
-
- bool GetNegationFlag(unsigned index) const {
- return ((negation_flags >> index) & 1) == 1;
- }
-
- BitField<32, 1, u64> flip;
- BitField<33, 1, u64> differential_mode;
-
- BitField<34, 3, u64> table_index_2;
- BitField<37, 3, u64> table_index_1;
-
- union {
- // delta value + base value
- BitField<40, 3, s64> db;
- BitField<43, 5, u64> b;
-
- BitField<48, 3, s64> dg;
- BitField<51, 5, u64> g;
-
- BitField<56, 3, s64> dr;
- BitField<59, 5, u64> r;
- } differential;
-
- union {
- BitField<40, 4, u64> b2;
- BitField<44, 4, u64> b1;
-
- BitField<48, 4, u64> g2;
- BitField<52, 4, u64> g1;
-
- BitField<56, 4, u64> r2;
- BitField<60, 4, u64> r1;
- } separate;
-
- const Math::Vec3<u8> GetRGB(int x, int y) const {
- int texel = 4 * x + y;
-
- if (flip)
- std::swap(x, y);
-
- // Lookup base value
- Math::Vec3<int> ret;
- if (differential_mode) {
- ret.r() = static_cast<int>(differential.r);
- ret.g() = static_cast<int>(differential.g);
- ret.b() = static_cast<int>(differential.b);
- if (x >= 2) {
- ret.r() += static_cast<int>(differential.dr);
- ret.g() += static_cast<int>(differential.dg);
- ret.b() += static_cast<int>(differential.db);
- }
- ret.r() = Color::Convert5To8(ret.r());
- ret.g() = Color::Convert5To8(ret.g());
- ret.b() = Color::Convert5To8(ret.b());
- } else {
- if (x < 2) {
- ret.r() = Color::Convert4To8(static_cast<u8>(separate.r1));
- ret.g() = Color::Convert4To8(static_cast<u8>(separate.g1));
- ret.b() = Color::Convert4To8(static_cast<u8>(separate.b1));
- } else {
- ret.r() = Color::Convert4To8(static_cast<u8>(separate.r2));
- ret.g() = Color::Convert4To8(static_cast<u8>(separate.g2));
- ret.b() = Color::Convert4To8(static_cast<u8>(separate.b2));
- }
- }
-
- // Add modifier
- unsigned table_index =
- static_cast<int>((x < 2) ? table_index_1.Value() : table_index_2.Value());
-
- static const std::array<std::array<u8, 2>, 8> etc1_modifier_table = {{
- {{2, 8}},
- {{5, 17}},
- {{9, 29}},
- {{13, 42}},
- {{18, 60}},
- {{24, 80}},
- {{33, 106}},
- {{47, 183}},
- }};
-
- int modifier = etc1_modifier_table.at(table_index).at(GetTableSubIndex(texel));
- if (GetNegationFlag(texel))
- modifier *= -1;
-
- ret.r() = MathUtil::Clamp(ret.r() + modifier, 0, 255);
- ret.g() = MathUtil::Clamp(ret.g() + modifier, 0, 255);
- ret.b() = MathUtil::Clamp(ret.b() + modifier, 0, 255);
-
- return ret.Cast<u8>();
- }
- } const* etc1_tile = reinterpret_cast<const ETC1Tile*>(source_ptr);
-
- alpha >>= 4 * ((x & 3) * 4 + (y & 3));
- return Math::MakeVec(etc1_tile->GetRGB(x & 3, y & 3),
- disable_alpha ? (u8)255 : Color::Convert4To8(alpha & 0xF));
- }
-
- default:
- LOG_ERROR(HW_GPU, "Unknown texture format: %x", (u32)info.format);
- DEBUG_ASSERT(false);
- return {};
- }
-}
-
-TextureInfo TextureInfo::FromPicaRegister(const Regs::TextureConfig& config,
- const Regs::TextureFormat& format) {
- TextureInfo info;
- info.physical_address = config.GetPhysicalAddress();
- info.width = config.width;
- info.height = config.height;
- info.format = format;
- info.stride = Pica::Regs::NibblesPerPixel(info.format) * info.width / 2;
- return info;
-}
-
#ifdef HAVE_PNG
// Adapter functions to libpng to write/flush to File::IOFile instances.
static void WriteIOFile(png_structp png_ptr, png_bytep data, png_size_t length) {
@@ -581,7 +333,7 @@ static void FlushIOFile(png_structp png_ptr) {
}
#endif
-void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
+void DumpTexture(const TexturingRegs::TextureConfig& texture_config, u8* data) {
#ifndef HAVE_PNG
return;
#else
@@ -642,12 +394,12 @@ void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) {
buf = new u8[row_stride * texture_config.height];
for (unsigned y = 0; y < texture_config.height; ++y) {
for (unsigned x = 0; x < texture_config.width; ++x) {
- TextureInfo info;
+ Pica::Texture::TextureInfo info;
info.width = texture_config.width;
info.height = texture_config.height;
info.stride = row_stride;
- info.format = g_state.regs.texture0_format;
- Math::Vec4<u8> texture_color = LookupTexture(data, x, y, info);
+ info.format = g_state.regs.texturing.texture0_format;
+ Math::Vec4<u8> texture_color = Pica::Texture::LookupTexture(data, x, y, info);
buf[3 * x + y * row_stride] = texture_color.r();
buf[3 * x + y * row_stride + 1] = texture_color.g();
buf[3 * x + y * row_stride + 2] = texture_color.b();
@@ -684,8 +436,10 @@ static std::string ReplacePattern(const std::string& input, const std::string& p
return ret;
}
-static std::string GetTevStageConfigSourceString(const Pica::Regs::TevStageConfig::Source& source) {
- using Source = Pica::Regs::TevStageConfig::Source;
+static std::string GetTevStageConfigSourceString(
+ const TexturingRegs::TevStageConfig::Source& source) {
+
+ using Source = TexturingRegs::TevStageConfig::Source;
static const std::map<Source, std::string> source_map = {
{Source::PrimaryColor, "PrimaryColor"},
{Source::PrimaryFragmentColor, "PrimaryFragmentColor"},
@@ -707,9 +461,10 @@ static std::string GetTevStageConfigSourceString(const Pica::Regs::TevStageConfi
}
static std::string GetTevStageConfigColorSourceString(
- const Pica::Regs::TevStageConfig::Source& source,
- const Pica::Regs::TevStageConfig::ColorModifier modifier) {
- using ColorModifier = Pica::Regs::TevStageConfig::ColorModifier;
+ const TexturingRegs::TevStageConfig::Source& source,
+ const TexturingRegs::TevStageConfig::ColorModifier modifier) {
+
+ using ColorModifier = TexturingRegs::TevStageConfig::ColorModifier;
static const std::map<ColorModifier, std::string> color_modifier_map = {
{ColorModifier::SourceColor, "%source.rgb"},
{ColorModifier::OneMinusSourceColor, "(1.0 - %source.rgb)"},
@@ -733,9 +488,10 @@ static std::string GetTevStageConfigColorSourceString(
}
static std::string GetTevStageConfigAlphaSourceString(
- const Pica::Regs::TevStageConfig::Source& source,
- const Pica::Regs::TevStageConfig::AlphaModifier modifier) {
- using AlphaModifier = Pica::Regs::TevStageConfig::AlphaModifier;
+ const TexturingRegs::TevStageConfig::Source& source,
+ const TexturingRegs::TevStageConfig::AlphaModifier modifier) {
+
+ using AlphaModifier = TexturingRegs::TevStageConfig::AlphaModifier;
static const std::map<AlphaModifier, std::string> alpha_modifier_map = {
{AlphaModifier::SourceAlpha, "%source.a"},
{AlphaModifier::OneMinusSourceAlpha, "(1.0 - %source.a)"},
@@ -757,8 +513,9 @@ static std::string GetTevStageConfigAlphaSourceString(
}
static std::string GetTevStageConfigOperationString(
- const Pica::Regs::TevStageConfig::Operation& operation) {
- using Operation = Pica::Regs::TevStageConfig::Operation;
+ const TexturingRegs::TevStageConfig::Operation& operation) {
+
+ using Operation = TexturingRegs::TevStageConfig::Operation;
static const std::map<Operation, std::string> combiner_map = {
{Operation::Replace, "%source1"},
{Operation::Modulate, "(%source1 * %source2)"},
@@ -778,7 +535,7 @@ static std::string GetTevStageConfigOperationString(
return op_it->second;
}
-std::string GetTevStageConfigColorCombinerString(const Pica::Regs::TevStageConfig& tev_stage) {
+std::string GetTevStageConfigColorCombinerString(const TexturingRegs::TevStageConfig& tev_stage) {
auto op_str = GetTevStageConfigOperationString(tev_stage.color_op);
op_str = ReplacePattern(
op_str, "%source1",
@@ -791,7 +548,7 @@ std::string GetTevStageConfigColorCombinerString(const Pica::Regs::TevStageConfi
GetTevStageConfigColorSourceString(tev_stage.color_source3, tev_stage.color_modifier3));
}
-std::string GetTevStageConfigAlphaCombinerString(const Pica::Regs::TevStageConfig& tev_stage) {
+std::string GetTevStageConfigAlphaCombinerString(const TexturingRegs::TevStageConfig& tev_stage) {
auto op_str = GetTevStageConfigOperationString(tev_stage.alpha_op);
op_str = ReplacePattern(
op_str, "%source1",
@@ -804,7 +561,7 @@ std::string GetTevStageConfigAlphaCombinerString(const Pica::Regs::TevStageConfi
GetTevStageConfigAlphaSourceString(tev_stage.alpha_source3, tev_stage.alpha_modifier3));
}
-void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig, 6>& stages) {
+void DumpTevStageConfig(const std::array<TexturingRegs::TevStageConfig, 6>& stages) {
std::string stage_info = "Tev setup:\n";
for (size_t index = 0; index < stages.size(); ++index) {
const auto& tev_stage = stages[index];
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h
index 46ea8d9c7..c1f29c527 100644
--- a/src/video_core/debug_utils/debug_utils.h
+++ b/src/video_core/debug_utils/debug_utils.h
@@ -17,7 +17,9 @@
#include <vector>
#include "common/common_types.h"
#include "common/vector_math.h"
-#include "video_core/pica.h"
+#include "video_core/regs_rasterizer.h"
+#include "video_core/regs_shader.h"
+#include "video_core/regs_texturing.h"
namespace CiTrace {
class Recorder;
@@ -85,7 +87,7 @@ public:
* @param data Optional data pointer (if unused, this is a nullptr)
* @note This function will perform nothing unless it is overridden in the child class.
*/
- virtual void OnPicaBreakPointHit(Event, void*) {}
+ virtual void OnPicaBreakPointHit(Event event, void* data) {}
/**
* Action to perform when emulation is resumed from a breakpoint.
@@ -182,9 +184,9 @@ namespace DebugUtils {
#define PICA_DUMP_TEXTURES 0
#define PICA_LOG_TEV 0
-void DumpShader(const std::string& filename, const Regs::ShaderConfig& config,
+void DumpShader(const std::string& filename, const ShaderRegs& config,
const Shader::ShaderSetup& setup,
- const Regs::VSOutputAttributes* output_attributes);
+ const RasterizerRegs::VSOutputAttributes* output_attributes);
// Utility class to log Pica commands.
struct PicaTrace {
@@ -205,38 +207,13 @@ inline bool IsPicaTracing() {
void OnPicaRegWrite(PicaTrace::Write write);
std::unique_ptr<PicaTrace> FinishPicaTracing();
-struct TextureInfo {
- PAddr physical_address;
- int width;
- int height;
- int stride;
- Pica::Regs::TextureFormat format;
+void DumpTexture(const TexturingRegs::TextureConfig& texture_config, u8* data);
- static TextureInfo FromPicaRegister(const Pica::Regs::TextureConfig& config,
- const Pica::Regs::TextureFormat& format);
-};
-
-/**
- * Lookup texel located at the given coordinates and return an RGBA vector of its color.
- * @param source Source pointer to read data from
- * @param s,t Texture coordinates to read from
- * @param info TextureInfo object describing the texture setup
- * @param disable_alpha This is used for debug widgets which use this method to display textures
- * without providing a good way to visualize alpha by themselves. If true, this will return 255 for
- * the alpha component, and either drop the information entirely or store it in an "unused" color
- * channel.
- * @todo Eventually we should get rid of the disable_alpha parameter.
- */
-const Math::Vec4<u8> LookupTexture(const u8* source, int s, int t, const TextureInfo& info,
- bool disable_alpha = false);
-
-void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data);
-
-std::string GetTevStageConfigColorCombinerString(const Pica::Regs::TevStageConfig& tev_stage);
-std::string GetTevStageConfigAlphaCombinerString(const Pica::Regs::TevStageConfig& tev_stage);
+std::string GetTevStageConfigColorCombinerString(const TexturingRegs::TevStageConfig& tev_stage);
+std::string GetTevStageConfigAlphaCombinerString(const TexturingRegs::TevStageConfig& tev_stage);
/// Dumps the Tev stage config to log at trace level
-void DumpTevStageConfig(const std::array<Pica::Regs::TevStageConfig, 6>& stages);
+void DumpTevStageConfig(const std::array<TexturingRegs::TevStageConfig, 6>& stages);
/**
* Used in the vertex loader to merge access records. TODO: Investigate if actually useful.
diff --git a/src/video_core/pica.cpp b/src/video_core/pica.cpp
index b4a77c632..b95148a6a 100644
--- a/src/video_core/pica.cpp
+++ b/src/video_core/pica.cpp
@@ -3,497 +3,14 @@
// Refer to the license.txt file included.
#include <cstring>
-#include <iterator>
-#include <unordered_map>
-#include <utility>
#include "video_core/pica.h"
#include "video_core/pica_state.h"
-#include "video_core/primitive_assembly.h"
-#include "video_core/shader/shader.h"
+#include "video_core/regs_pipeline.h"
namespace Pica {
State g_state;
-static const std::pair<u16, const char*> register_names[] = {
- {0x010, "GPUREG_FINALIZE"},
-
- {0x040, "GPUREG_FACECULLING_CONFIG"},
- {0x041, "GPUREG_VIEWPORT_WIDTH"},
- {0x042, "GPUREG_VIEWPORT_INVW"},
- {0x043, "GPUREG_VIEWPORT_HEIGHT"},
- {0x044, "GPUREG_VIEWPORT_INVH"},
-
- {0x047, "GPUREG_FRAGOP_CLIP"},
- {0x048, "GPUREG_FRAGOP_CLIP_DATA0"},
- {0x049, "GPUREG_FRAGOP_CLIP_DATA1"},
- {0x04A, "GPUREG_FRAGOP_CLIP_DATA2"},
- {0x04B, "GPUREG_FRAGOP_CLIP_DATA3"},
-
- {0x04D, "GPUREG_DEPTHMAP_SCALE"},
- {0x04E, "GPUREG_DEPTHMAP_OFFSET"},
- {0x04F, "GPUREG_SH_OUTMAP_TOTAL"},
- {0x050, "GPUREG_SH_OUTMAP_O0"},
- {0x051, "GPUREG_SH_OUTMAP_O1"},
- {0x052, "GPUREG_SH_OUTMAP_O2"},
- {0x053, "GPUREG_SH_OUTMAP_O3"},
- {0x054, "GPUREG_SH_OUTMAP_O4"},
- {0x055, "GPUREG_SH_OUTMAP_O5"},
- {0x056, "GPUREG_SH_OUTMAP_O6"},
-
- {0x061, "GPUREG_EARLYDEPTH_FUNC"},
- {0x062, "GPUREG_EARLYDEPTH_TEST1"},
- {0x063, "GPUREG_EARLYDEPTH_CLEAR"},
- {0x064, "GPUREG_SH_OUTATTR_MODE"},
- {0x065, "GPUREG_SCISSORTEST_MODE"},
- {0x066, "GPUREG_SCISSORTEST_POS"},
- {0x067, "GPUREG_SCISSORTEST_DIM"},
- {0x068, "GPUREG_VIEWPORT_XY"},
-
- {0x06A, "GPUREG_EARLYDEPTH_DATA"},
-
- {0x06D, "GPUREG_DEPTHMAP_ENABLE"},
- {0x06E, "GPUREG_RENDERBUF_DIM"},
- {0x06F, "GPUREG_SH_OUTATTR_CLOCK"},
-
- {0x080, "GPUREG_TEXUNIT_CONFIG"},
- {0x081, "GPUREG_TEXUNIT0_BORDER_COLOR"},
- {0x082, "GPUREG_TEXUNIT0_DIM"},
- {0x083, "GPUREG_TEXUNIT0_PARAM"},
- {0x084, "GPUREG_TEXUNIT0_LOD"},
- {0x085, "GPUREG_TEXUNIT0_ADDR1"},
- {0x086, "GPUREG_TEXUNIT0_ADDR2"},
- {0x087, "GPUREG_TEXUNIT0_ADDR3"},
- {0x088, "GPUREG_TEXUNIT0_ADDR4"},
- {0x089, "GPUREG_TEXUNIT0_ADDR5"},
- {0x08A, "GPUREG_TEXUNIT0_ADDR6"},
- {0x08B, "GPUREG_TEXUNIT0_SHADOW"},
-
- {0x08E, "GPUREG_TEXUNIT0_TYPE"},
- {0x08F, "GPUREG_LIGHTING_ENABLE0"},
-
- {0x091, "GPUREG_TEXUNIT1_BORDER_COLOR"},
- {0x092, "GPUREG_TEXUNIT1_DIM"},
- {0x093, "GPUREG_TEXUNIT1_PARAM"},
- {0x094, "GPUREG_TEXUNIT1_LOD"},
- {0x095, "GPUREG_TEXUNIT1_ADDR"},
- {0x096, "GPUREG_TEXUNIT1_TYPE"},
-
- {0x099, "GPUREG_TEXUNIT2_BORDER_COLOR"},
- {0x09A, "GPUREG_TEXUNIT2_DIM"},
- {0x09B, "GPUREG_TEXUNIT2_PARAM"},
- {0x09C, "GPUREG_TEXUNIT2_LOD"},
- {0x09D, "GPUREG_TEXUNIT2_ADDR"},
- {0x09E, "GPUREG_TEXUNIT2_TYPE"},
-
- {0x0A8, "GPUREG_TEXUNIT3_PROCTEX0"},
- {0x0A9, "GPUREG_TEXUNIT3_PROCTEX1"},
- {0x0AA, "GPUREG_TEXUNIT3_PROCTEX2"},
- {0x0AB, "GPUREG_TEXUNIT3_PROCTEX3"},
- {0x0AC, "GPUREG_TEXUNIT3_PROCTEX4"},
- {0x0AD, "GPUREG_TEXUNIT3_PROCTEX5"},
-
- {0x0AF, "GPUREG_PROCTEX_LUT"},
- {0x0B0, "GPUREG_PROCTEX_LUT_DATA0"},
- {0x0B1, "GPUREG_PROCTEX_LUT_DATA1"},
- {0x0B2, "GPUREG_PROCTEX_LUT_DATA2"},
- {0x0B3, "GPUREG_PROCTEX_LUT_DATA3"},
- {0x0B4, "GPUREG_PROCTEX_LUT_DATA4"},
- {0x0B5, "GPUREG_PROCTEX_LUT_DATA5"},
- {0x0B6, "GPUREG_PROCTEX_LUT_DATA6"},
- {0x0B7, "GPUREG_PROCTEX_LUT_DATA7"},
-
- {0x0C0, "GPUREG_TEXENV0_SOURCE"},
- {0x0C1, "GPUREG_TEXENV0_OPERAND"},
- {0x0C2, "GPUREG_TEXENV0_COMBINER"},
- {0x0C3, "GPUREG_TEXENV0_COLOR"},
- {0x0C4, "GPUREG_TEXENV0_SCALE"},
-
- {0x0C8, "GPUREG_TEXENV1_SOURCE"},
- {0x0C9, "GPUREG_TEXENV1_OPERAND"},
- {0x0CA, "GPUREG_TEXENV1_COMBINER"},
- {0x0CB, "GPUREG_TEXENV1_COLOR"},
- {0x0CC, "GPUREG_TEXENV1_SCALE"},
-
- {0x0D0, "GPUREG_TEXENV2_SOURCE"},
- {0x0D1, "GPUREG_TEXENV2_OPERAND"},
- {0x0D2, "GPUREG_TEXENV2_COMBINER"},
- {0x0D3, "GPUREG_TEXENV2_COLOR"},
- {0x0D4, "GPUREG_TEXENV2_SCALE"},
-
- {0x0D8, "GPUREG_TEXENV3_SOURCE"},
- {0x0D9, "GPUREG_TEXENV3_OPERAND"},
- {0x0DA, "GPUREG_TEXENV3_COMBINER"},
- {0x0DB, "GPUREG_TEXENV3_COLOR"},
- {0x0DC, "GPUREG_TEXENV3_SCALE"},
-
- {0x0E0, "GPUREG_TEXENV_UPDATE_BUFFER"},
- {0x0E1, "GPUREG_FOG_COLOR"},
-
- {0x0E4, "GPUREG_GAS_ATTENUATION"},
- {0x0E5, "GPUREG_GAS_ACCMAX"},
- {0x0E6, "GPUREG_FOG_LUT_INDEX"},
-
- {0x0E8, "GPUREG_FOG_LUT_DATA0"},
- {0x0E9, "GPUREG_FOG_LUT_DATA1"},
- {0x0EA, "GPUREG_FOG_LUT_DATA2"},
- {0x0EB, "GPUREG_FOG_LUT_DATA3"},
- {0x0EC, "GPUREG_FOG_LUT_DATA4"},
- {0x0ED, "GPUREG_FOG_LUT_DATA5"},
- {0x0EE, "GPUREG_FOG_LUT_DATA6"},
- {0x0EF, "GPUREG_FOG_LUT_DATA7"},
- {0x0F0, "GPUREG_TEXENV4_SOURCE"},
- {0x0F1, "GPUREG_TEXENV4_OPERAND"},
- {0x0F2, "GPUREG_TEXENV4_COMBINER"},
- {0x0F3, "GPUREG_TEXENV4_COLOR"},
- {0x0F4, "GPUREG_TEXENV4_SCALE"},
-
- {0x0F8, "GPUREG_TEXENV5_SOURCE"},
- {0x0F9, "GPUREG_TEXENV5_OPERAND"},
- {0x0FA, "GPUREG_TEXENV5_COMBINER"},
- {0x0FB, "GPUREG_TEXENV5_COLOR"},
- {0x0FC, "GPUREG_TEXENV5_SCALE"},
- {0x0FD, "GPUREG_TEXENV_BUFFER_COLOR"},
-
- {0x100, "GPUREG_COLOR_OPERATION"},
- {0x101, "GPUREG_BLEND_FUNC"},
- {0x102, "GPUREG_LOGIC_OP"},
- {0x103, "GPUREG_BLEND_COLOR"},
- {0x104, "GPUREG_FRAGOP_ALPHA_TEST"},
- {0x105, "GPUREG_STENCIL_TEST"},
- {0x106, "GPUREG_STENCIL_OP"},
- {0x107, "GPUREG_DEPTH_COLOR_MASK"},
-
- {0x110, "GPUREG_FRAMEBUFFER_INVALIDATE"},
- {0x111, "GPUREG_FRAMEBUFFER_FLUSH"},
- {0x112, "GPUREG_COLORBUFFER_READ"},
- {0x113, "GPUREG_COLORBUFFER_WRITE"},
- {0x114, "GPUREG_DEPTHBUFFER_READ"},
- {0x115, "GPUREG_DEPTHBUFFER_WRITE"},
- {0x116, "GPUREG_DEPTHBUFFER_FORMAT"},
- {0x117, "GPUREG_COLORBUFFER_FORMAT"},
- {0x118, "GPUREG_EARLYDEPTH_TEST2"},
-
- {0x11B, "GPUREG_FRAMEBUFFER_BLOCK32"},
- {0x11C, "GPUREG_DEPTHBUFFER_LOC"},
- {0x11D, "GPUREG_COLORBUFFER_LOC"},
- {0x11E, "GPUREG_FRAMEBUFFER_DIM"},
-
- {0x120, "GPUREG_GAS_LIGHT_XY"},
- {0x121, "GPUREG_GAS_LIGHT_Z"},
- {0x122, "GPUREG_GAS_LIGHT_Z_COLOR"},
- {0x123, "GPUREG_GAS_LUT_INDEX"},
- {0x124, "GPUREG_GAS_LUT_DATA"},
-
- {0x126, "GPUREG_GAS_DELTAZ_DEPTH"},
-
- {0x130, "GPUREG_FRAGOP_SHADOW"},
-
- {0x140, "GPUREG_LIGHT0_SPECULAR0"},
- {0x141, "GPUREG_LIGHT0_SPECULAR1"},
- {0x142, "GPUREG_LIGHT0_DIFFUSE"},
- {0x143, "GPUREG_LIGHT0_AMBIENT"},
- {0x144, "GPUREG_LIGHT0_XY"},
- {0x145, "GPUREG_LIGHT0_Z"},
- {0x146, "GPUREG_LIGHT0_SPOTDIR_XY"},
- {0x147, "GPUREG_LIGHT0_SPOTDIR_Z"},
-
- {0x149, "GPUREG_LIGHT0_CONFIG"},
- {0x14A, "GPUREG_LIGHT0_ATTENUATION_BIAS"},
- {0x14B, "GPUREG_LIGHT0_ATTENUATION_SCALE"},
-
- {0x150, "GPUREG_LIGHT1_SPECULAR0"},
- {0x151, "GPUREG_LIGHT1_SPECULAR1"},
- {0x152, "GPUREG_LIGHT1_DIFFUSE"},
- {0x153, "GPUREG_LIGHT1_AMBIENT"},
- {0x154, "GPUREG_LIGHT1_XY"},
- {0x155, "GPUREG_LIGHT1_Z"},
- {0x156, "GPUREG_LIGHT1_SPOTDIR_XY"},
- {0x157, "GPUREG_LIGHT1_SPOTDIR_Z"},
-
- {0x159, "GPUREG_LIGHT1_CONFIG"},
- {0x15A, "GPUREG_LIGHT1_ATTENUATION_BIAS"},
- {0x15B, "GPUREG_LIGHT1_ATTENUATION_SCALE"},
-
- {0x160, "GPUREG_LIGHT2_SPECULAR0"},
- {0x161, "GPUREG_LIGHT2_SPECULAR1"},
- {0x162, "GPUREG_LIGHT2_DIFFUSE"},
- {0x163, "GPUREG_LIGHT2_AMBIENT"},
- {0x164, "GPUREG_LIGHT2_XY"},
- {0x165, "GPUREG_LIGHT2_Z"},
- {0x166, "GPUREG_LIGHT2_SPOTDIR_XY"},
- {0x167, "GPUREG_LIGHT2_SPOTDIR_Z"},
-
- {0x169, "GPUREG_LIGHT2_CONFIG"},
- {0x16A, "GPUREG_LIGHT2_ATTENUATION_BIAS"},
- {0x16B, "GPUREG_LIGHT2_ATTENUATION_SCALE"},
-
- {0x170, "GPUREG_LIGHT3_SPECULAR0"},
- {0x171, "GPUREG_LIGHT3_SPECULAR1"},
- {0x172, "GPUREG_LIGHT3_DIFFUSE"},
- {0x173, "GPUREG_LIGHT3_AMBIENT"},
- {0x174, "GPUREG_LIGHT3_XY"},
- {0x175, "GPUREG_LIGHT3_Z"},
- {0x176, "GPUREG_LIGHT3_SPOTDIR_XY"},
- {0x177, "GPUREG_LIGHT3_SPOTDIR_Z"},
-
- {0x179, "GPUREG_LIGHT3_CONFIG"},
- {0x17A, "GPUREG_LIGHT3_ATTENUATION_BIAS"},
- {0x17B, "GPUREG_LIGHT3_ATTENUATION_SCALE"},
-
- {0x180, "GPUREG_LIGHT4_SPECULAR0"},
- {0x181, "GPUREG_LIGHT4_SPECULAR1"},
- {0x182, "GPUREG_LIGHT4_DIFFUSE"},
- {0x183, "GPUREG_LIGHT4_AMBIENT"},
- {0x184, "GPUREG_LIGHT4_XY"},
- {0x185, "GPUREG_LIGHT4_Z"},
- {0x186, "GPUREG_LIGHT4_SPOTDIR_XY"},
- {0x187, "GPUREG_LIGHT4_SPOTDIR_Z"},
-
- {0x189, "GPUREG_LIGHT4_CONFIG"},
- {0x18A, "GPUREG_LIGHT4_ATTENUATION_BIAS"},
- {0x18B, "GPUREG_LIGHT4_ATTENUATION_SCALE"},
-
- {0x190, "GPUREG_LIGHT5_SPECULAR0"},
- {0x191, "GPUREG_LIGHT5_SPECULAR1"},
- {0x192, "GPUREG_LIGHT5_DIFFUSE"},
- {0x193, "GPUREG_LIGHT5_AMBIENT"},
- {0x194, "GPUREG_LIGHT5_XY"},
- {0x195, "GPUREG_LIGHT5_Z"},
- {0x196, "GPUREG_LIGHT5_SPOTDIR_XY"},
- {0x197, "GPUREG_LIGHT5_SPOTDIR_Z"},
-
- {0x199, "GPUREG_LIGHT5_CONFIG"},
- {0x19A, "GPUREG_LIGHT5_ATTENUATION_BIAS"},
- {0x19B, "GPUREG_LIGHT5_ATTENUATION_SCALE"},
-
- {0x1A0, "GPUREG_LIGHT6_SPECULAR0"},
- {0x1A1, "GPUREG_LIGHT6_SPECULAR1"},
- {0x1A2, "GPUREG_LIGHT6_DIFFUSE"},
- {0x1A3, "GPUREG_LIGHT6_AMBIENT"},
- {0x1A4, "GPUREG_LIGHT6_XY"},
- {0x1A5, "GPUREG_LIGHT6_Z"},
- {0x1A6, "GPUREG_LIGHT6_SPOTDIR_XY"},
- {0x1A7, "GPUREG_LIGHT6_SPOTDIR_Z"},
-
- {0x1A9, "GPUREG_LIGHT6_CONFIG"},
- {0x1AA, "GPUREG_LIGHT6_ATTENUATION_BIAS"},
- {0x1AB, "GPUREG_LIGHT6_ATTENUATION_SCALE"},
-
- {0x1B0, "GPUREG_LIGHT7_SPECULAR0"},
- {0x1B1, "GPUREG_LIGHT7_SPECULAR1"},
- {0x1B2, "GPUREG_LIGHT7_DIFFUSE"},
- {0x1B3, "GPUREG_LIGHT7_AMBIENT"},
- {0x1B4, "GPUREG_LIGHT7_XY"},
- {0x1B5, "GPUREG_LIGHT7_Z"},
- {0x1B6, "GPUREG_LIGHT7_SPOTDIR_XY"},
- {0x1B7, "GPUREG_LIGHT7_SPOTDIR_Z"},
-
- {0x1B9, "GPUREG_LIGHT7_CONFIG"},
- {0x1BA, "GPUREG_LIGHT7_ATTENUATION_BIAS"},
- {0x1BB, "GPUREG_LIGHT7_ATTENUATION_SCALE"},
-
- {0x1C0, "GPUREG_LIGHTING_AMBIENT"},
-
- {0x1C2, "GPUREG_LIGHTING_NUM_LIGHTS"},
- {0x1C3, "GPUREG_LIGHTING_CONFIG0"},
- {0x1C4, "GPUREG_LIGHTING_CONFIG1"},
- {0x1C5, "GPUREG_LIGHTING_LUT_INDEX"},
- {0x1C6, "GPUREG_LIGHTING_ENABLE1"},
-
- {0x1C8, "GPUREG_LIGHTING_LUT_DATA0"},
- {0x1C9, "GPUREG_LIGHTING_LUT_DATA1"},
- {0x1CA, "GPUREG_LIGHTING_LUT_DATA2"},
- {0x1CB, "GPUREG_LIGHTING_LUT_DATA3"},
- {0x1CC, "GPUREG_LIGHTING_LUT_DATA4"},
- {0x1CD, "GPUREG_LIGHTING_LUT_DATA5"},
- {0x1CE, "GPUREG_LIGHTING_LUT_DATA6"},
- {0x1CF, "GPUREG_LIGHTING_LUT_DATA7"},
- {0x1D0, "GPUREG_LIGHTING_LUTINPUT_ABS"},
- {0x1D1, "GPUREG_LIGHTING_LUTINPUT_SELECT"},
- {0x1D2, "GPUREG_LIGHTING_LUTINPUT_SCALE"},
-
- {0x1D9, "GPUREG_LIGHTING_LIGHT_PERMUTATION"},
-
- {0x200, "GPUREG_ATTRIBBUFFERS_LOC"},
- {0x201, "GPUREG_ATTRIBBUFFERS_FORMAT_LOW"},
- {0x202, "GPUREG_ATTRIBBUFFERS_FORMAT_HIGH"},
- {0x203, "GPUREG_ATTRIBBUFFER0_OFFSET"},
- {0x204, "GPUREG_ATTRIBBUFFER0_CONFIG1"},
- {0x205, "GPUREG_ATTRIBBUFFER0_CONFIG2"},
- {0x206, "GPUREG_ATTRIBBUFFER1_OFFSET"},
- {0x207, "GPUREG_ATTRIBBUFFER1_CONFIG1"},
- {0x208, "GPUREG_ATTRIBBUFFER1_CONFIG2"},
- {0x209, "GPUREG_ATTRIBBUFFER2_OFFSET"},
- {0x20A, "GPUREG_ATTRIBBUFFER2_CONFIG1"},
- {0x20B, "GPUREG_ATTRIBBUFFER2_CONFIG2"},
- {0x20C, "GPUREG_ATTRIBBUFFER3_OFFSET"},
- {0x20D, "GPUREG_ATTRIBBUFFER3_CONFIG1"},
- {0x20E, "GPUREG_ATTRIBBUFFER3_CONFIG2"},
- {0x20F, "GPUREG_ATTRIBBUFFER4_OFFSET"},
- {0x210, "GPUREG_ATTRIBBUFFER4_CONFIG1"},
- {0x211, "GPUREG_ATTRIBBUFFER4_CONFIG2"},
- {0x212, "GPUREG_ATTRIBBUFFER5_OFFSET"},
- {0x213, "GPUREG_ATTRIBBUFFER5_CONFIG1"},
- {0x214, "GPUREG_ATTRIBBUFFER5_CONFIG2"},
- {0x215, "GPUREG_ATTRIBBUFFER6_OFFSET"},
- {0x216, "GPUREG_ATTRIBBUFFER6_CONFIG1"},
- {0x217, "GPUREG_ATTRIBBUFFER6_CONFIG2"},
- {0x218, "GPUREG_ATTRIBBUFFER7_OFFSET"},
- {0x219, "GPUREG_ATTRIBBUFFER7_CONFIG1"},
- {0x21A, "GPUREG_ATTRIBBUFFER7_CONFIG2"},
- {0x21B, "GPUREG_ATTRIBBUFFER8_OFFSET"},
- {0x21C, "GPUREG_ATTRIBBUFFER8_CONFIG1"},
- {0x21D, "GPUREG_ATTRIBBUFFER8_CONFIG2"},
- {0x21E, "GPUREG_ATTRIBBUFFER9_OFFSET"},
- {0x21F, "GPUREG_ATTRIBBUFFER9_CONFIG1"},
- {0x220, "GPUREG_ATTRIBBUFFER9_CONFIG2"},
- {0x221, "GPUREG_ATTRIBBUFFER10_OFFSET"},
- {0x222, "GPUREG_ATTRIBBUFFER10_CONFIG1"},
- {0x223, "GPUREG_ATTRIBBUFFER10_CONFIG2"},
- {0x224, "GPUREG_ATTRIBBUFFER11_OFFSET"},
- {0x225, "GPUREG_ATTRIBBUFFER11_CONFIG1"},
- {0x226, "GPUREG_ATTRIBBUFFER11_CONFIG2"},
- {0x227, "GPUREG_INDEXBUFFER_CONFIG"},
- {0x228, "GPUREG_NUMVERTICES"},
- {0x229, "GPUREG_GEOSTAGE_CONFIG"},
- {0x22A, "GPUREG_VERTEX_OFFSET"},
-
- {0x22D, "GPUREG_POST_VERTEX_CACHE_NUM"},
- {0x22E, "GPUREG_DRAWARRAYS"},
- {0x22F, "GPUREG_DRAWELEMENTS"},
-
- {0x231, "GPUREG_VTX_FUNC"},
- {0x232, "GPUREG_FIXEDATTRIB_INDEX"},
- {0x233, "GPUREG_FIXEDATTRIB_DATA0"},
- {0x234, "GPUREG_FIXEDATTRIB_DATA1"},
- {0x235, "GPUREG_FIXEDATTRIB_DATA2"},
-
- {0x238, "GPUREG_CMDBUF_SIZE0"},
- {0x239, "GPUREG_CMDBUF_SIZE1"},
- {0x23A, "GPUREG_CMDBUF_ADDR0"},
- {0x23B, "GPUREG_CMDBUF_ADDR1"},
- {0x23C, "GPUREG_CMDBUF_JUMP0"},
- {0x23D, "GPUREG_CMDBUF_JUMP1"},
-
- {0x242, "GPUREG_VSH_NUM_ATTR"},
-
- {0x244, "GPUREG_VSH_COM_MODE"},
- {0x245, "GPUREG_START_DRAW_FUNC0"},
-
- {0x24A, "GPUREG_VSH_OUTMAP_TOTAL1"},
-
- {0x251, "GPUREG_VSH_OUTMAP_TOTAL2"},
- {0x252, "GPUREG_GSH_MISC0"},
- {0x253, "GPUREG_GEOSTAGE_CONFIG2"},
- {0x254, "GPUREG_GSH_MISC1"},
-
- {0x25E, "GPUREG_PRIMITIVE_CONFIG"},
- {0x25F, "GPUREG_RESTART_PRIMITIVE"},
-
- {0x280, "GPUREG_GSH_BOOLUNIFORM"},
- {0x281, "GPUREG_GSH_INTUNIFORM_I0"},
- {0x282, "GPUREG_GSH_INTUNIFORM_I1"},
- {0x283, "GPUREG_GSH_INTUNIFORM_I2"},
- {0x284, "GPUREG_GSH_INTUNIFORM_I3"},
-
- {0x289, "GPUREG_GSH_INPUTBUFFER_CONFIG"},
- {0x28A, "GPUREG_GSH_ENTRYPOINT"},
- {0x28B, "GPUREG_GSH_ATTRIBUTES_PERMUTATION_LOW"},
- {0x28C, "GPUREG_GSH_ATTRIBUTES_PERMUTATION_HIGH"},
- {0x28D, "GPUREG_GSH_OUTMAP_MASK"},
-
- {0x28F, "GPUREG_GSH_CODETRANSFER_END"},
- {0x290, "GPUREG_GSH_FLOATUNIFORM_INDEX"},
- {0x291, "GPUREG_GSH_FLOATUNIFORM_DATA0"},
- {0x292, "GPUREG_GSH_FLOATUNIFORM_DATA1"},
- {0x293, "GPUREG_GSH_FLOATUNIFORM_DATA2"},
- {0x294, "GPUREG_GSH_FLOATUNIFORM_DATA3"},
- {0x295, "GPUREG_GSH_FLOATUNIFORM_DATA4"},
- {0x296, "GPUREG_GSH_FLOATUNIFORM_DATA5"},
- {0x297, "GPUREG_GSH_FLOATUNIFORM_DATA6"},
- {0x298, "GPUREG_GSH_FLOATUNIFORM_DATA7"},
-
- {0x29B, "GPUREG_GSH_CODETRANSFER_INDEX"},
- {0x29C, "GPUREG_GSH_CODETRANSFER_DATA0"},
- {0x29D, "GPUREG_GSH_CODETRANSFER_DATA1"},
- {0x29E, "GPUREG_GSH_CODETRANSFER_DATA2"},
- {0x29F, "GPUREG_GSH_CODETRANSFER_DATA3"},
- {0x2A0, "GPUREG_GSH_CODETRANSFER_DATA4"},
- {0x2A1, "GPUREG_GSH_CODETRANSFER_DATA5"},
- {0x2A2, "GPUREG_GSH_CODETRANSFER_DATA6"},
- {0x2A3, "GPUREG_GSH_CODETRANSFER_DATA7"},
-
- {0x2A5, "GPUREG_GSH_OPDESCS_INDEX"},
- {0x2A6, "GPUREG_GSH_OPDESCS_DATA0"},
- {0x2A7, "GPUREG_GSH_OPDESCS_DATA1"},
- {0x2A8, "GPUREG_GSH_OPDESCS_DATA2"},
- {0x2A9, "GPUREG_GSH_OPDESCS_DATA3"},
- {0x2AA, "GPUREG_GSH_OPDESCS_DATA4"},
- {0x2AB, "GPUREG_GSH_OPDESCS_DATA5"},
- {0x2AC, "GPUREG_GSH_OPDESCS_DATA6"},
- {0x2AD, "GPUREG_GSH_OPDESCS_DATA7"},
-
- {0x2B0, "GPUREG_VSH_BOOLUNIFORM"},
- {0x2B1, "GPUREG_VSH_INTUNIFORM_I0"},
- {0x2B2, "GPUREG_VSH_INTUNIFORM_I1"},
- {0x2B3, "GPUREG_VSH_INTUNIFORM_I2"},
- {0x2B4, "GPUREG_VSH_INTUNIFORM_I3"},
-
- {0x2B9, "GPUREG_VSH_INPUTBUFFER_CONFIG"},
- {0x2BA, "GPUREG_VSH_ENTRYPOINT"},
- {0x2BB, "GPUREG_VSH_ATTRIBUTES_PERMUTATION_LOW"},
- {0x2BC, "GPUREG_VSH_ATTRIBUTES_PERMUTATION_HIGH"},
- {0x2BD, "GPUREG_VSH_OUTMAP_MASK"},
-
- {0x2BF, "GPUREG_VSH_CODETRANSFER_END"},
- {0x2C0, "GPUREG_VSH_FLOATUNIFORM_INDEX"},
- {0x2C1, "GPUREG_VSH_FLOATUNIFORM_DATA0"},
- {0x2C2, "GPUREG_VSH_FLOATUNIFORM_DATA1"},
- {0x2C3, "GPUREG_VSH_FLOATUNIFORM_DATA2"},
- {0x2C4, "GPUREG_VSH_FLOATUNIFORM_DATA3"},
- {0x2C5, "GPUREG_VSH_FLOATUNIFORM_DATA4"},
- {0x2C6, "GPUREG_VSH_FLOATUNIFORM_DATA5"},
- {0x2C7, "GPUREG_VSH_FLOATUNIFORM_DATA6"},
- {0x2C8, "GPUREG_VSH_FLOATUNIFORM_DATA7"},
-
- {0x2CB, "GPUREG_VSH_CODETRANSFER_INDEX"},
- {0x2CC, "GPUREG_VSH_CODETRANSFER_DATA0"},
- {0x2CD, "GPUREG_VSH_CODETRANSFER_DATA1"},
- {0x2CE, "GPUREG_VSH_CODETRANSFER_DATA2"},
- {0x2CF, "GPUREG_VSH_CODETRANSFER_DATA3"},
- {0x2D0, "GPUREG_VSH_CODETRANSFER_DATA4"},
- {0x2D1, "GPUREG_VSH_CODETRANSFER_DATA5"},
- {0x2D2, "GPUREG_VSH_CODETRANSFER_DATA6"},
- {0x2D3, "GPUREG_VSH_CODETRANSFER_DATA7"},
-
- {0x2D5, "GPUREG_VSH_OPDESCS_INDEX"},
- {0x2D6, "GPUREG_VSH_OPDESCS_DATA0"},
- {0x2D7, "GPUREG_VSH_OPDESCS_DATA1"},
- {0x2D8, "GPUREG_VSH_OPDESCS_DATA2"},
- {0x2D9, "GPUREG_VSH_OPDESCS_DATA3"},
- {0x2DA, "GPUREG_VSH_OPDESCS_DATA4"},
- {0x2DB, "GPUREG_VSH_OPDESCS_DATA5"},
- {0x2DC, "GPUREG_VSH_OPDESCS_DATA6"},
- {0x2DD, "GPUREG_VSH_OPDESCS_DATA7"},
-};
-
-std::string Regs::GetCommandName(int index) {
- static std::unordered_map<u32, const char*> map;
-
- if (map.empty()) {
- map.insert(std::begin(register_names), std::end(register_names));
- }
-
- // Return empty string if no match is found
- auto it = map.find(index);
- if (it != map.end()) {
- return it->second;
- } else {
- return std::string();
- }
-}
-
void Init() {
g_state.Reset();
}
@@ -513,6 +30,6 @@ void State::Reset() {
Zero(gs);
Zero(cmd_list);
Zero(immediate);
- primitive_assembler.Reconfigure(Regs::TriangleTopology::List);
+ primitive_assembler.Reconfigure(PipelineRegs::TriangleTopology::List);
}
}
diff --git a/src/video_core/pica.h b/src/video_core/pica.h
index b2db609ec..dc8aa6670 100644
--- a/src/video_core/pica.h
+++ b/src/video_core/pica.h
@@ -4,1412 +4,9 @@
#pragma once
-#include <array>
-#include <cstddef>
-#include <string>
-
-#ifndef _MSC_VER
-#include <type_traits> // for std::enable_if
-#endif
-
-#include "common/assert.h"
-#include "common/bit_field.h"
-#include "common/common_funcs.h"
-#include "common/common_types.h"
-#include "common/logging/log.h"
-#include "common/vector_math.h"
-
+#include "video_core/regs_texturing.h"
namespace Pica {
-// Returns index corresponding to the Regs member labeled by field_name
-// TODO: Due to Visual studio bug 209229, offsetof does not return constant expressions
-// when used with array elements (e.g. PICA_REG_INDEX(vs_uniform_setup.set_value[1])).
-// For details cf.
-// https://connect.microsoft.com/VisualStudio/feedback/details/209229/offsetof-does-not-produce-a-constant-expression-for-array-members
-// Hopefully, this will be fixed sometime in the future.
-// For lack of better alternatives, we currently hardcode the offsets when constant
-// expressions are needed via PICA_REG_INDEX_WORKAROUND (on sane compilers, static_asserts
-// will then make sure the offsets indeed match the automatically calculated ones).
-#define PICA_REG_INDEX(field_name) (offsetof(Pica::Regs, field_name) / sizeof(u32))
-#if defined(_MSC_VER)
-#define PICA_REG_INDEX_WORKAROUND(field_name, backup_workaround_index) (backup_workaround_index)
-#else
-// NOTE: Yeah, hacking in a static_assert here just to workaround the lacking MSVC compiler
-// really is this annoying. This macro just forwards its first argument to PICA_REG_INDEX
-// and then performs a (no-op) cast to size_t iff the second argument matches the expected
-// field offset. Otherwise, the compiler will fail to compile this code.
-#define PICA_REG_INDEX_WORKAROUND(field_name, backup_workaround_index) \
- ((typename std::enable_if<backup_workaround_index == PICA_REG_INDEX(field_name), \
- size_t>::type)PICA_REG_INDEX(field_name))
-#endif // _MSC_VER
-
-struct Regs {
-
- INSERT_PADDING_WORDS(0x10);
-
- u32 trigger_irq;
-
- INSERT_PADDING_WORDS(0x2f);
-
- enum class CullMode : u32 {
- // Select which polygons are considered to be "frontfacing".
- KeepAll = 0,
- KeepClockWise = 1,
- KeepCounterClockWise = 2,
- // TODO: What does the third value imply?
- };
-
- union {
- BitField<0, 2, CullMode> cull_mode;
- };
-
- BitField<0, 24, u32> viewport_size_x;
-
- INSERT_PADDING_WORDS(0x1);
-
- BitField<0, 24, u32> viewport_size_y;
-
- INSERT_PADDING_WORDS(0x9);
-
- BitField<0, 24, u32> viewport_depth_range; // float24
- BitField<0, 24, u32> viewport_depth_near_plane; // float24
-
- BitField<0, 3, u32> vs_output_total;
-
- union VSOutputAttributes {
- // Maps components of output vertex attributes to semantics
- enum Semantic : u32 {
- POSITION_X = 0,
- POSITION_Y = 1,
- POSITION_Z = 2,
- POSITION_W = 3,
-
- QUATERNION_X = 4,
- QUATERNION_Y = 5,
- QUATERNION_Z = 6,
- QUATERNION_W = 7,
-
- COLOR_R = 8,
- COLOR_G = 9,
- COLOR_B = 10,
- COLOR_A = 11,
-
- TEXCOORD0_U = 12,
- TEXCOORD0_V = 13,
- TEXCOORD1_U = 14,
- TEXCOORD1_V = 15,
-
- // TODO: Not verified
- VIEW_X = 18,
- VIEW_Y = 19,
- VIEW_Z = 20,
-
- TEXCOORD2_U = 22,
- TEXCOORD2_V = 23,
-
- INVALID = 31,
- };
-
- BitField<0, 5, Semantic> map_x;
- BitField<8, 5, Semantic> map_y;
- BitField<16, 5, Semantic> map_z;
- BitField<24, 5, Semantic> map_w;
- } vs_output_attributes[7];
-
- INSERT_PADDING_WORDS(0xe);
-
- enum class ScissorMode : u32 {
- Disabled = 0,
- Exclude = 1, // Exclude pixels inside the scissor box
-
- Include = 3 // Exclude pixels outside the scissor box
- };
-
- struct {
- BitField<0, 2, ScissorMode> mode;
-
- union {
- BitField<0, 16, u32> x1;
- BitField<16, 16, u32> y1;
- };
-
- union {
- BitField<0, 16, u32> x2;
- BitField<16, 16, u32> y2;
- };
- } scissor_test;
-
- union {
- BitField<0, 10, s32> x;
- BitField<16, 10, s32> y;
- } viewport_corner;
-
- INSERT_PADDING_WORDS(0x1);
-
- // TODO: early depth
- INSERT_PADDING_WORDS(0x1);
-
- INSERT_PADDING_WORDS(0x2);
-
- enum DepthBuffering : u32 {
- WBuffering = 0,
- ZBuffering = 1,
- };
- BitField<0, 1, DepthBuffering> depthmap_enable;
-
- INSERT_PADDING_WORDS(0x12);
-
- struct TextureConfig {
- enum TextureType : u32 {
- Texture2D = 0,
- TextureCube = 1,
- Shadow2D = 2,
- Projection2D = 3,
- ShadowCube = 4,
- Disabled = 5,
- };
-
- enum WrapMode : u32 {
- ClampToEdge = 0,
- ClampToBorder = 1,
- Repeat = 2,
- MirroredRepeat = 3,
- };
-
- enum TextureFilter : u32 {
- Nearest = 0,
- Linear = 1,
- };
-
- union {
- u32 raw;
- BitField<0, 8, u32> r;
- BitField<8, 8, u32> g;
- BitField<16, 8, u32> b;
- BitField<24, 8, u32> a;
- } border_color;
-
- union {
- BitField<0, 16, u32> height;
- BitField<16, 16, u32> width;
- };
-
- union {
- BitField<1, 1, TextureFilter> mag_filter;
- BitField<2, 1, TextureFilter> min_filter;
- BitField<8, 2, WrapMode> wrap_t;
- BitField<12, 2, WrapMode> wrap_s;
- BitField<28, 2, TextureType>
- type; ///< @note Only valid for texture 0 according to 3DBrew.
- };
-
- INSERT_PADDING_WORDS(0x1);
-
- u32 address;
-
- u32 GetPhysicalAddress() const {
- return DecodeAddressRegister(address);
- }
-
- // texture1 and texture2 store the texture format directly after the address
- // whereas texture0 inserts some additional flags inbetween.
- // Hence, we store the format separately so that all other parameters can be described
- // in a single structure.
- };
-
- enum class TextureFormat : u32 {
- RGBA8 = 0,
- RGB8 = 1,
- RGB5A1 = 2,
- RGB565 = 3,
- RGBA4 = 4,
- IA8 = 5,
- RG8 = 6, ///< @note Also called HILO8 in 3DBrew.
- I8 = 7,
- A8 = 8,
- IA4 = 9,
- I4 = 10,
- A4 = 11,
- ETC1 = 12, // compressed
- ETC1A4 = 13, // compressed
- };
-
- enum class LogicOp : u32 {
- Clear = 0,
- And = 1,
- AndReverse = 2,
- Copy = 3,
- Set = 4,
- CopyInverted = 5,
- NoOp = 6,
- Invert = 7,
- Nand = 8,
- Or = 9,
- Nor = 10,
- Xor = 11,
- Equiv = 12,
- AndInverted = 13,
- OrReverse = 14,
- OrInverted = 15,
- };
-
- static unsigned NibblesPerPixel(TextureFormat format) {
- switch (format) {
- case TextureFormat::RGBA8:
- return 8;
-
- case TextureFormat::RGB8:
- return 6;
-
- case TextureFormat::RGB5A1:
- case TextureFormat::RGB565:
- case TextureFormat::RGBA4:
- case TextureFormat::IA8:
- case TextureFormat::RG8:
- return 4;
-
- case TextureFormat::I4:
- case TextureFormat::A4:
- return 1;
-
- case TextureFormat::I8:
- case TextureFormat::A8:
- case TextureFormat::IA4:
- default: // placeholder for yet unknown formats
- return 2;
- }
- }
-
- union {
- BitField<0, 1, u32> texture0_enable;
- BitField<1, 1, u32> texture1_enable;
- BitField<2, 1, u32> texture2_enable;
- };
- TextureConfig texture0;
- INSERT_PADDING_WORDS(0x8);
- BitField<0, 4, TextureFormat> texture0_format;
- BitField<0, 1, u32> fragment_lighting_enable;
- INSERT_PADDING_WORDS(0x1);
- TextureConfig texture1;
- BitField<0, 4, TextureFormat> texture1_format;
- INSERT_PADDING_WORDS(0x2);
- TextureConfig texture2;
- BitField<0, 4, TextureFormat> texture2_format;
- INSERT_PADDING_WORDS(0x21);
-
- struct FullTextureConfig {
- const bool enabled;
- const TextureConfig config;
- const TextureFormat format;
- };
- const std::array<FullTextureConfig, 3> GetTextures() const {
- return {{
- {texture0_enable.ToBool(), texture0, texture0_format},
- {texture1_enable.ToBool(), texture1, texture1_format},
- {texture2_enable.ToBool(), texture2, texture2_format},
- }};
- }
-
- // 0xc0-0xff: Texture Combiner (akin to glTexEnv)
- struct TevStageConfig {
- enum class Source : u32 {
- PrimaryColor = 0x0,
- PrimaryFragmentColor = 0x1,
- SecondaryFragmentColor = 0x2,
-
- Texture0 = 0x3,
- Texture1 = 0x4,
- Texture2 = 0x5,
- Texture3 = 0x6,
-
- PreviousBuffer = 0xd,
- Constant = 0xe,
- Previous = 0xf,
- };
-
- enum class ColorModifier : u32 {
- SourceColor = 0x0,
- OneMinusSourceColor = 0x1,
- SourceAlpha = 0x2,
- OneMinusSourceAlpha = 0x3,
- SourceRed = 0x4,
- OneMinusSourceRed = 0x5,
-
- SourceGreen = 0x8,
- OneMinusSourceGreen = 0x9,
-
- SourceBlue = 0xc,
- OneMinusSourceBlue = 0xd,
- };
-
- enum class AlphaModifier : u32 {
- SourceAlpha = 0x0,
- OneMinusSourceAlpha = 0x1,
- SourceRed = 0x2,
- OneMinusSourceRed = 0x3,
- SourceGreen = 0x4,
- OneMinusSourceGreen = 0x5,
- SourceBlue = 0x6,
- OneMinusSourceBlue = 0x7,
- };
-
- enum class Operation : u32 {
- Replace = 0,
- Modulate = 1,
- Add = 2,
- AddSigned = 3,
- Lerp = 4,
- Subtract = 5,
- Dot3_RGB = 6,
-
- MultiplyThenAdd = 8,
- AddThenMultiply = 9,
- };
-
- union {
- u32 sources_raw;
- BitField<0, 4, Source> color_source1;
- BitField<4, 4, Source> color_source2;
- BitField<8, 4, Source> color_source3;
- BitField<16, 4, Source> alpha_source1;
- BitField<20, 4, Source> alpha_source2;
- BitField<24, 4, Source> alpha_source3;
- };
-
- union {
- u32 modifiers_raw;
- BitField<0, 4, ColorModifier> color_modifier1;
- BitField<4, 4, ColorModifier> color_modifier2;
- BitField<8, 4, ColorModifier> color_modifier3;
- BitField<12, 3, AlphaModifier> alpha_modifier1;
- BitField<16, 3, AlphaModifier> alpha_modifier2;
- BitField<20, 3, AlphaModifier> alpha_modifier3;
- };
-
- union {
- u32 ops_raw;
- BitField<0, 4, Operation> color_op;
- BitField<16, 4, Operation> alpha_op;
- };
-
- union {
- u32 const_color;
- BitField<0, 8, u32> const_r;
- BitField<8, 8, u32> const_g;
- BitField<16, 8, u32> const_b;
- BitField<24, 8, u32> const_a;
- };
-
- union {
- u32 scales_raw;
- BitField<0, 2, u32> color_scale;
- BitField<16, 2, u32> alpha_scale;
- };
-
- inline unsigned GetColorMultiplier() const {
- return (color_scale < 3) ? (1 << color_scale) : 1;
- }
-
- inline unsigned GetAlphaMultiplier() const {
- return (alpha_scale < 3) ? (1 << alpha_scale) : 1;
- }
- };
-
- TevStageConfig tev_stage0;
- INSERT_PADDING_WORDS(0x3);
- TevStageConfig tev_stage1;
- INSERT_PADDING_WORDS(0x3);
- TevStageConfig tev_stage2;
- INSERT_PADDING_WORDS(0x3);
- TevStageConfig tev_stage3;
- INSERT_PADDING_WORDS(0x3);
-
- enum class FogMode : u32 {
- None = 0,
- Fog = 5,
- Gas = 7,
- };
-
- union {
- BitField<0, 3, FogMode> fog_mode;
- BitField<16, 1, u32> fog_flip;
-
- union {
- // Tev stages 0-3 write their output to the combiner buffer if the corresponding bit in
- // these masks are set
- BitField<8, 4, u32> update_mask_rgb;
- BitField<12, 4, u32> update_mask_a;
-
- bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const {
- return (stage_index < 4) && (update_mask_rgb & (1 << stage_index));
- }
-
- bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const {
- return (stage_index < 4) && (update_mask_a & (1 << stage_index));
- }
- } tev_combiner_buffer_input;
- };
-
- union {
- u32 raw;
- BitField<0, 8, u32> r;
- BitField<8, 8, u32> g;
- BitField<16, 8, u32> b;
- } fog_color;
-
- INSERT_PADDING_WORDS(0x4);
-
- BitField<0, 16, u32> fog_lut_offset;
-
- INSERT_PADDING_WORDS(0x1);
-
- u32 fog_lut_data[8];
-
- TevStageConfig tev_stage4;
- INSERT_PADDING_WORDS(0x3);
- TevStageConfig tev_stage5;
-
- union {
- u32 raw;
- BitField<0, 8, u32> r;
- BitField<8, 8, u32> g;
- BitField<16, 8, u32> b;
- BitField<24, 8, u32> a;
- } tev_combiner_buffer_color;
-
- INSERT_PADDING_WORDS(0x2);
-
- const std::array<Regs::TevStageConfig, 6> GetTevStages() const {
- return {{tev_stage0, tev_stage1, tev_stage2, tev_stage3, tev_stage4, tev_stage5}};
- };
-
- enum class BlendEquation : u32 {
- Add = 0,
- Subtract = 1,
- ReverseSubtract = 2,
- Min = 3,
- Max = 4,
- };
-
- enum class BlendFactor : u32 {
- Zero = 0,
- One = 1,
- SourceColor = 2,
- OneMinusSourceColor = 3,
- DestColor = 4,
- OneMinusDestColor = 5,
- SourceAlpha = 6,
- OneMinusSourceAlpha = 7,
- DestAlpha = 8,
- OneMinusDestAlpha = 9,
- ConstantColor = 10,
- OneMinusConstantColor = 11,
- ConstantAlpha = 12,
- OneMinusConstantAlpha = 13,
- SourceAlphaSaturate = 14,
- };
-
- enum class CompareFunc : u32 {
- Never = 0,
- Always = 1,
- Equal = 2,
- NotEqual = 3,
- LessThan = 4,
- LessThanOrEqual = 5,
- GreaterThan = 6,
- GreaterThanOrEqual = 7,
- };
-
- enum class StencilAction : u32 {
- Keep = 0,
- Zero = 1,
- Replace = 2,
- Increment = 3,
- Decrement = 4,
- Invert = 5,
- IncrementWrap = 6,
- DecrementWrap = 7,
- };
-
- struct {
- union {
- // If false, logic blending is used
- BitField<8, 1, u32> alphablend_enable;
- };
-
- union {
- BitField<0, 8, BlendEquation> blend_equation_rgb;
- BitField<8, 8, BlendEquation> blend_equation_a;
-
- BitField<16, 4, BlendFactor> factor_source_rgb;
- BitField<20, 4, BlendFactor> factor_dest_rgb;
-
- BitField<24, 4, BlendFactor> factor_source_a;
- BitField<28, 4, BlendFactor> factor_dest_a;
- } alpha_blending;
-
- union {
- BitField<0, 4, LogicOp> logic_op;
- };
-
- union {
- u32 raw;
- BitField<0, 8, u32> r;
- BitField<8, 8, u32> g;
- BitField<16, 8, u32> b;
- BitField<24, 8, u32> a;
- } blend_const;
-
- union {
- BitField<0, 1, u32> enable;
- BitField<4, 3, CompareFunc> func;
- BitField<8, 8, u32> ref;
- } alpha_test;
-
- struct {
- union {
- // Raw value of this register
- u32 raw_func;
-
- // If true, enable stencil testing
- BitField<0, 1, u32> enable;
-
- // Comparison operation for stencil testing
- BitField<4, 3, CompareFunc> func;
-
- // Mask used to control writing to the stencil buffer
- BitField<8, 8, u32> write_mask;
-
- // Value to compare against for stencil testing
- BitField<16, 8, u32> reference_value;
-
- // Mask to apply on stencil test inputs
- BitField<24, 8, u32> input_mask;
- };
-
- union {
- // Raw value of this register
- u32 raw_op;
-
- // Action to perform when the stencil test fails
- BitField<0, 3, StencilAction> action_stencil_fail;
-
- // Action to perform when stencil testing passed but depth testing fails
- BitField<4, 3, StencilAction> action_depth_fail;
-
- // Action to perform when both stencil and depth testing pass
- BitField<8, 3, StencilAction> action_depth_pass;
- };
- } stencil_test;
-
- union {
- BitField<0, 1, u32> depth_test_enable;
- BitField<4, 3, CompareFunc> depth_test_func;
- BitField<8, 1, u32> red_enable;
- BitField<9, 1, u32> green_enable;
- BitField<10, 1, u32> blue_enable;
- BitField<11, 1, u32> alpha_enable;
- BitField<12, 1, u32> depth_write_enable;
- };
-
- INSERT_PADDING_WORDS(0x8);
- } output_merger;
-
- // Components are laid out in reverse byte order, most significant bits first.
- enum class ColorFormat : u32 {
- RGBA8 = 0,
- RGB8 = 1,
- RGB5A1 = 2,
- RGB565 = 3,
- RGBA4 = 4,
- };
-
- enum class DepthFormat : u32 {
- D16 = 0,
- D24 = 2,
- D24S8 = 3,
- };
-
- // Returns the number of bytes in the specified color format
- static unsigned BytesPerColorPixel(ColorFormat format) {
- switch (format) {
- case ColorFormat::RGBA8:
- return 4;
- case ColorFormat::RGB8:
- return 3;
- case ColorFormat::RGB5A1:
- case ColorFormat::RGB565:
- case ColorFormat::RGBA4:
- return 2;
- default:
- LOG_CRITICAL(HW_GPU, "Unknown color format %u", format);
- UNIMPLEMENTED();
- }
- }
-
- struct FramebufferConfig {
- INSERT_PADDING_WORDS(0x3);
-
- union {
- BitField<0, 4, u32> allow_color_write; // 0 = disable, else enable
- };
-
- INSERT_PADDING_WORDS(0x1);
-
- union {
- BitField<0, 2, u32> allow_depth_stencil_write; // 0 = disable, else enable
- };
-
- DepthFormat depth_format; // TODO: Should be a BitField!
- BitField<16, 3, ColorFormat> color_format;
-
- INSERT_PADDING_WORDS(0x4);
-
- u32 depth_buffer_address;
- u32 color_buffer_address;
-
- union {
- // Apparently, the framebuffer width is stored as expected,
- // while the height is stored as the actual height minus one.
- // Hence, don't access these fields directly but use the accessors
- // GetWidth() and GetHeight() instead.
- BitField<0, 11, u32> width;
- BitField<12, 10, u32> height;
- };
-
- INSERT_PADDING_WORDS(0x1);
-
- inline u32 GetColorBufferPhysicalAddress() const {
- return DecodeAddressRegister(color_buffer_address);
- }
- inline u32 GetDepthBufferPhysicalAddress() const {
- return DecodeAddressRegister(depth_buffer_address);
- }
-
- inline u32 GetWidth() const {
- return width;
- }
-
- inline u32 GetHeight() const {
- return height + 1;
- }
- } framebuffer;
-
- // Returns the number of bytes in the specified depth format
- static u32 BytesPerDepthPixel(DepthFormat format) {
- switch (format) {
- case DepthFormat::D16:
- return 2;
- case DepthFormat::D24:
- return 3;
- case DepthFormat::D24S8:
- return 4;
- default:
- LOG_CRITICAL(HW_GPU, "Unknown depth format %u", format);
- UNIMPLEMENTED();
- }
- }
-
- // Returns the number of bits per depth component of the specified depth format
- static u32 DepthBitsPerPixel(DepthFormat format) {
- switch (format) {
- case DepthFormat::D16:
- return 16;
- case DepthFormat::D24:
- case DepthFormat::D24S8:
- return 24;
- default:
- LOG_CRITICAL(HW_GPU, "Unknown depth format %u", format);
- UNIMPLEMENTED();
- }
- }
-
- INSERT_PADDING_WORDS(0x20);
-
- enum class LightingSampler {
- Distribution0 = 0,
- Distribution1 = 1,
- Fresnel = 3,
- ReflectBlue = 4,
- ReflectGreen = 5,
- ReflectRed = 6,
- SpotlightAttenuation = 8,
- DistanceAttenuation = 16,
- };
-
- /**
- * Pica fragment lighting supports using different LUTs for each lighting component:
- * Reflectance R, G, and B channels, distribution function for specular components 0 and 1,
- * fresnel factor, and spotlight attenuation. Furthermore, which LUTs are used for each channel
- * (or whether a channel is enabled at all) is specified by various pre-defined lighting
- * configurations. With configurations that require more LUTs, more cycles are required on HW to
- * perform lighting computations.
- */
- enum class LightingConfig {
- Config0 = 0, ///< Reflect Red, Distribution 0, Spotlight
- Config1 = 1, ///< Reflect Red, Fresnel, Spotlight
- Config2 = 2, ///< Reflect Red, Distribution 0/1
- Config3 = 3, ///< Distribution 0/1, Fresnel
- Config4 = 4, ///< Reflect Red/Green/Blue, Distribution 0/1, Spotlight
- Config5 = 5, ///< Reflect Red/Green/Blue, Distribution 0, Fresnel, Spotlight
- Config6 = 6, ///< Reflect Red, Distribution 0/1, Fresnel, Spotlight
- Config7 = 8, ///< Reflect Red/Green/Blue, Distribution 0/1, Fresnel, Spotlight
- ///< NOTE: '8' is intentional, '7' does not appear to be a valid configuration
- };
-
- /// Selects which lighting components are affected by fresnel
- enum class LightingFresnelSelector {
- None = 0, ///< Fresnel is disabled
- PrimaryAlpha = 1, ///< Primary (diffuse) lighting alpha is affected by fresnel
- SecondaryAlpha = 2, ///< Secondary (specular) lighting alpha is affected by fresnel
- Both =
- PrimaryAlpha |
- SecondaryAlpha, ///< Both primary and secondary lighting alphas are affected by fresnel
- };
-
- /// Factor used to scale the output of a lighting LUT
- enum class LightingScale {
- Scale1 = 0, ///< Scale is 1x
- Scale2 = 1, ///< Scale is 2x
- Scale4 = 2, ///< Scale is 4x
- Scale8 = 3, ///< Scale is 8x
- Scale1_4 = 6, ///< Scale is 0.25x
- Scale1_2 = 7, ///< Scale is 0.5x
- };
-
- enum class LightingLutInput {
- NH = 0, // Cosine of the angle between the normal and half-angle vectors
- VH = 1, // Cosine of the angle between the view and half-angle vectors
- NV = 2, // Cosine of the angle between the normal and the view vector
- LN = 3, // Cosine of the angle between the light and the normal vectors
- };
-
- enum class LightingBumpMode : u32 {
- None = 0,
- NormalMap = 1,
- TangentMap = 2,
- };
-
- union LightColor {
- BitField<0, 10, u32> b;
- BitField<10, 10, u32> g;
- BitField<20, 10, u32> r;
-
- Math::Vec3f ToVec3f() const {
- // These fields are 10 bits wide, however 255 corresponds to 1.0f for each color
- // component
- return Math::MakeVec((f32)r / 255.f, (f32)g / 255.f, (f32)b / 255.f);
- }
- };
-
- /// Returns true if the specified lighting sampler is supported by the current Pica lighting
- /// configuration
- static bool IsLightingSamplerSupported(LightingConfig config, LightingSampler sampler) {
- switch (sampler) {
- case LightingSampler::Distribution0:
- return (config != LightingConfig::Config1);
-
- case LightingSampler::Distribution1:
- return (config != LightingConfig::Config0) && (config != LightingConfig::Config1) &&
- (config != LightingConfig::Config5);
-
- case LightingSampler::Fresnel:
- return (config != LightingConfig::Config0) && (config != LightingConfig::Config2) &&
- (config != LightingConfig::Config4);
-
- case LightingSampler::ReflectRed:
- return (config != LightingConfig::Config3);
-
- case LightingSampler::ReflectGreen:
- case LightingSampler::ReflectBlue:
- return (config == LightingConfig::Config4) || (config == LightingConfig::Config5) ||
- (config == LightingConfig::Config7);
- default:
- UNREACHABLE_MSG("Regs::IsLightingSamplerSupported: Reached "
- "unreachable section, sampler should be one "
- "of Distribution0, Distribution1, Fresnel, "
- "ReflectRed, ReflectGreen or ReflectBlue, instead "
- "got %i",
- static_cast<int>(config));
- }
- }
-
- struct {
- struct LightSrc {
- LightColor specular_0; // material.specular_0 * light.specular_0
- LightColor specular_1; // material.specular_1 * light.specular_1
- LightColor diffuse; // material.diffuse * light.diffuse
- LightColor ambient; // material.ambient * light.ambient
-
- // Encoded as 16-bit floating point
- union {
- BitField<0, 16, u32> x;
- BitField<16, 16, u32> y;
- };
- union {
- BitField<0, 16, u32> z;
- };
-
- INSERT_PADDING_WORDS(0x3);
-
- union {
- BitField<0, 1, u32> directional;
- BitField<1, 1, u32> two_sided_diffuse; // When disabled, clamp dot-product to 0
- } config;
-
- BitField<0, 20, u32> dist_atten_bias;
- BitField<0, 20, u32> dist_atten_scale;
-
- INSERT_PADDING_WORDS(0x4);
- };
- static_assert(sizeof(LightSrc) == 0x10 * sizeof(u32),
- "LightSrc structure must be 0x10 words");
-
- LightSrc light[8];
- LightColor global_ambient; // Emission + (material.ambient * lighting.ambient)
- INSERT_PADDING_WORDS(0x1);
- BitField<0, 3, u32> num_lights; // Number of enabled lights - 1
-
- union {
- BitField<2, 2, LightingFresnelSelector> fresnel_selector;
- BitField<4, 4, LightingConfig> config;
- BitField<22, 2, u32> bump_selector; // 0: Texture 0, 1: Texture 1, 2: Texture 2
- BitField<27, 1, u32> clamp_highlights;
- BitField<28, 2, LightingBumpMode> bump_mode;
- BitField<30, 1, u32> disable_bump_renorm;
- } config0;
-
- union {
- BitField<16, 1, u32> disable_lut_d0;
- BitField<17, 1, u32> disable_lut_d1;
- BitField<19, 1, u32> disable_lut_fr;
- BitField<20, 1, u32> disable_lut_rr;
- BitField<21, 1, u32> disable_lut_rg;
- BitField<22, 1, u32> disable_lut_rb;
-
- // Each bit specifies whether distance attenuation should be applied for the
- // corresponding light
-
- BitField<24, 1, u32> disable_dist_atten_light_0;
- BitField<25, 1, u32> disable_dist_atten_light_1;
- BitField<26, 1, u32> disable_dist_atten_light_2;
- BitField<27, 1, u32> disable_dist_atten_light_3;
- BitField<28, 1, u32> disable_dist_atten_light_4;
- BitField<29, 1, u32> disable_dist_atten_light_5;
- BitField<30, 1, u32> disable_dist_atten_light_6;
- BitField<31, 1, u32> disable_dist_atten_light_7;
- } config1;
-
- bool IsDistAttenDisabled(unsigned index) const {
- const unsigned disable[] = {
- config1.disable_dist_atten_light_0, config1.disable_dist_atten_light_1,
- config1.disable_dist_atten_light_2, config1.disable_dist_atten_light_3,
- config1.disable_dist_atten_light_4, config1.disable_dist_atten_light_5,
- config1.disable_dist_atten_light_6, config1.disable_dist_atten_light_7};
- return disable[index] != 0;
- }
-
- union {
- BitField<0, 8, u32> index; ///< Index at which to set data in the LUT
- BitField<8, 5, u32> type; ///< Type of LUT for which to set data
- } lut_config;
-
- BitField<0, 1, u32> disable;
- INSERT_PADDING_WORDS(0x1);
-
- // When data is written to any of these registers, it gets written to the lookup table of
- // the selected type at the selected index, specified above in the `lut_config` register.
- // With each write, `lut_config.index` is incremented. It does not matter which of these
- // registers is written to, the behavior will be the same.
- u32 lut_data[8];
-
- // These are used to specify if absolute (abs) value should be used for each LUT index. When
- // abs mode is disabled, LUT indexes are in the range of (-1.0, 1.0). Otherwise, they are in
- // the range of (0.0, 1.0).
- union {
- BitField<1, 1, u32> disable_d0;
- BitField<5, 1, u32> disable_d1;
- BitField<9, 1, u32> disable_sp;
- BitField<13, 1, u32> disable_fr;
- BitField<17, 1, u32> disable_rb;
- BitField<21, 1, u32> disable_rg;
- BitField<25, 1, u32> disable_rr;
- } abs_lut_input;
-
- union {
- BitField<0, 3, LightingLutInput> d0;
- BitField<4, 3, LightingLutInput> d1;
- BitField<8, 3, LightingLutInput> sp;
- BitField<12, 3, LightingLutInput> fr;
- BitField<16, 3, LightingLutInput> rb;
- BitField<20, 3, LightingLutInput> rg;
- BitField<24, 3, LightingLutInput> rr;
- } lut_input;
-
- union {
- BitField<0, 3, LightingScale> d0;
- BitField<4, 3, LightingScale> d1;
- BitField<8, 3, LightingScale> sp;
- BitField<12, 3, LightingScale> fr;
- BitField<16, 3, LightingScale> rb;
- BitField<20, 3, LightingScale> rg;
- BitField<24, 3, LightingScale> rr;
-
- static float GetScale(LightingScale scale) {
- switch (scale) {
- case LightingScale::Scale1:
- return 1.0f;
- case LightingScale::Scale2:
- return 2.0f;
- case LightingScale::Scale4:
- return 4.0f;
- case LightingScale::Scale8:
- return 8.0f;
- case LightingScale::Scale1_4:
- return 0.25f;
- case LightingScale::Scale1_2:
- return 0.5f;
- }
- return 0.0f;
- }
- } lut_scale;
-
- INSERT_PADDING_WORDS(0x6);
-
- union {
- // There are 8 light enable "slots", corresponding to the total number of lights
- // supported by Pica. For N enabled lights (specified by register 0x1c2, or 'src_num'
- // above), the first N slots below will be set to integers within the range of 0-7,
- // corresponding to the actual light that is enabled for each slot.
-
- BitField<0, 3, u32> slot_0;
- BitField<4, 3, u32> slot_1;
- BitField<8, 3, u32> slot_2;
- BitField<12, 3, u32> slot_3;
- BitField<16, 3, u32> slot_4;
- BitField<20, 3, u32> slot_5;
- BitField<24, 3, u32> slot_6;
- BitField<28, 3, u32> slot_7;
-
- unsigned GetNum(unsigned index) const {
- const unsigned enable_slots[] = {slot_0, slot_1, slot_2, slot_3,
- slot_4, slot_5, slot_6, slot_7};
- return enable_slots[index];
- }
- } light_enable;
- } lighting;
-
- INSERT_PADDING_WORDS(0x26);
-
- enum class VertexAttributeFormat : u64 {
- BYTE = 0,
- UBYTE = 1,
- SHORT = 2,
- FLOAT = 3,
- };
-
- struct {
- BitField<0, 29, u32> base_address;
-
- u32 GetPhysicalBaseAddress() const {
- return DecodeAddressRegister(base_address);
- }
-
- // Descriptor for internal vertex attributes
- union {
- BitField<0, 2, VertexAttributeFormat> format0; // size of one element
- BitField<2, 2, u64> size0; // number of elements minus 1
- BitField<4, 2, VertexAttributeFormat> format1;
- BitField<6, 2, u64> size1;
- BitField<8, 2, VertexAttributeFormat> format2;
- BitField<10, 2, u64> size2;
- BitField<12, 2, VertexAttributeFormat> format3;
- BitField<14, 2, u64> size3;
- BitField<16, 2, VertexAttributeFormat> format4;
- BitField<18, 2, u64> size4;
- BitField<20, 2, VertexAttributeFormat> format5;
- BitField<22, 2, u64> size5;
- BitField<24, 2, VertexAttributeFormat> format6;
- BitField<26, 2, u64> size6;
- BitField<28, 2, VertexAttributeFormat> format7;
- BitField<30, 2, u64> size7;
- BitField<32, 2, VertexAttributeFormat> format8;
- BitField<34, 2, u64> size8;
- BitField<36, 2, VertexAttributeFormat> format9;
- BitField<38, 2, u64> size9;
- BitField<40, 2, VertexAttributeFormat> format10;
- BitField<42, 2, u64> size10;
- BitField<44, 2, VertexAttributeFormat> format11;
- BitField<46, 2, u64> size11;
-
- BitField<48, 12, u64> attribute_mask;
-
- // number of total attributes minus 1
- BitField<60, 4, u64> num_extra_attributes;
- };
-
- inline VertexAttributeFormat GetFormat(int n) const {
- VertexAttributeFormat formats[] = {format0, format1, format2, format3,
- format4, format5, format6, format7,
- format8, format9, format10, format11};
- return formats[n];
- }
-
- inline int GetNumElements(int n) const {
- u64 sizes[] = {size0, size1, size2, size3, size4, size5,
- size6, size7, size8, size9, size10, size11};
- return (int)sizes[n] + 1;
- }
-
- inline int GetElementSizeInBytes(int n) const {
- return (GetFormat(n) == VertexAttributeFormat::FLOAT)
- ? 4
- : (GetFormat(n) == VertexAttributeFormat::SHORT) ? 2 : 1;
- }
-
- inline int GetStride(int n) const {
- return GetNumElements(n) * GetElementSizeInBytes(n);
- }
-
- inline bool IsDefaultAttribute(int id) const {
- return (id >= 12) || (attribute_mask & (1ULL << id)) != 0;
- }
-
- inline int GetNumTotalAttributes() const {
- return (int)num_extra_attributes + 1;
- }
-
- // Attribute loaders map the source vertex data to input attributes
- // This e.g. allows to load different attributes from different memory locations
- struct {
- // Source attribute data offset from the base address
- u32 data_offset;
-
- union {
- BitField<0, 4, u64> comp0;
- BitField<4, 4, u64> comp1;
- BitField<8, 4, u64> comp2;
- BitField<12, 4, u64> comp3;
- BitField<16, 4, u64> comp4;
- BitField<20, 4, u64> comp5;
- BitField<24, 4, u64> comp6;
- BitField<28, 4, u64> comp7;
- BitField<32, 4, u64> comp8;
- BitField<36, 4, u64> comp9;
- BitField<40, 4, u64> comp10;
- BitField<44, 4, u64> comp11;
-
- // bytes for a single vertex in this loader
- BitField<48, 8, u64> byte_count;
-
- BitField<60, 4, u64> component_count;
- };
-
- inline int GetComponent(int n) const {
- u64 components[] = {comp0, comp1, comp2, comp3, comp4, comp5,
- comp6, comp7, comp8, comp9, comp10, comp11};
- return (int)components[n];
- }
- } attribute_loaders[12];
- } vertex_attributes;
-
- struct {
- enum IndexFormat : u32 {
- BYTE = 0,
- SHORT = 1,
- };
-
- union {
- BitField<0, 31, u32> offset; // relative to base attribute address
- BitField<31, 1, IndexFormat> format;
- };
- } index_array;
-
- // Number of vertices to render
- u32 num_vertices;
-
- INSERT_PADDING_WORDS(0x1);
-
- // The index of the first vertex to render
- u32 vertex_offset;
-
- INSERT_PADDING_WORDS(0x3);
-
- // These two trigger rendering of triangles
- u32 trigger_draw;
- u32 trigger_draw_indexed;
-
- INSERT_PADDING_WORDS(0x2);
-
- // These registers are used to setup the default "fall-back" vertex shader attributes
- struct {
- // Index of the current default attribute
- u32 index;
-
- // Writing to these registers sets the "current" default attribute.
- u32 set_value[3];
- } vs_default_attributes_setup;
-
- INSERT_PADDING_WORDS(0x2);
-
- struct {
- // There are two channels that can be used to configure the next command buffer, which
- // can be then executed by writing to the "trigger" registers. There are two reasons why a
- // game might use this feature:
- // 1) With this, an arbitrary number of additional command buffers may be executed in
- // sequence without requiring any intervention of the CPU after the initial one is
- // kicked off.
- // 2) Games can configure these registers to provide a command list subroutine mechanism.
-
- BitField<0, 20, u32> size[2]; ///< Size (in bytes / 8) of each channel's command buffer
- BitField<0, 28, u32> addr[2]; ///< Physical address / 8 of each channel's command buffer
- u32 trigger[2]; ///< Triggers execution of the channel's command buffer when written to
-
- unsigned GetSize(unsigned index) const {
- ASSERT(index < 2);
- return 8 * size[index];
- }
-
- PAddr GetPhysicalAddress(unsigned index) const {
- ASSERT(index < 2);
- return (PAddr)(8 * addr[index]);
- }
- } command_buffer;
-
- INSERT_PADDING_WORDS(0x07);
-
- enum class GPUMode : u32 {
- Drawing = 0,
- Configuring = 1,
- };
-
- GPUMode gpu_mode;
-
- INSERT_PADDING_WORDS(0x18);
-
- enum class TriangleTopology : u32 {
- List = 0,
- Strip = 1,
- Fan = 2,
- Shader = 3, // Programmable setup unit implemented in a geometry shader
- };
-
- BitField<8, 2, TriangleTopology> triangle_topology;
-
- u32 restart_primitive;
-
- INSERT_PADDING_WORDS(0x20);
-
- struct ShaderConfig {
- BitField<0, 16, u32> bool_uniforms;
-
- union {
- BitField<0, 8, u32> x;
- BitField<8, 8, u32> y;
- BitField<16, 8, u32> z;
- BitField<24, 8, u32> w;
- } int_uniforms[4];
-
- INSERT_PADDING_WORDS(0x4);
-
- union {
- // Number of input attributes to shader unit - 1
- BitField<0, 4, u32> num_input_attributes;
- };
-
- // Offset to shader program entry point (in words)
- BitField<0, 16, u32> main_offset;
-
- union {
- BitField<0, 4, u64> attribute0_register;
- BitField<4, 4, u64> attribute1_register;
- BitField<8, 4, u64> attribute2_register;
- BitField<12, 4, u64> attribute3_register;
- BitField<16, 4, u64> attribute4_register;
- BitField<20, 4, u64> attribute5_register;
- BitField<24, 4, u64> attribute6_register;
- BitField<28, 4, u64> attribute7_register;
- BitField<32, 4, u64> attribute8_register;
- BitField<36, 4, u64> attribute9_register;
- BitField<40, 4, u64> attribute10_register;
- BitField<44, 4, u64> attribute11_register;
- BitField<48, 4, u64> attribute12_register;
- BitField<52, 4, u64> attribute13_register;
- BitField<56, 4, u64> attribute14_register;
- BitField<60, 4, u64> attribute15_register;
-
- int GetRegisterForAttribute(int attribute_index) const {
- u64 fields[] = {
- attribute0_register, attribute1_register, attribute2_register,
- attribute3_register, attribute4_register, attribute5_register,
- attribute6_register, attribute7_register, attribute8_register,
- attribute9_register, attribute10_register, attribute11_register,
- attribute12_register, attribute13_register, attribute14_register,
- attribute15_register,
- };
- return (int)fields[attribute_index];
- }
- } input_register_map;
-
- BitField<0, 16, u32> output_mask;
-
- // 0x28E, CODETRANSFER_END
- INSERT_PADDING_WORDS(0x2);
-
- struct {
- enum Format : u32 {
- FLOAT24 = 0,
- FLOAT32 = 1,
- };
-
- bool IsFloat32() const {
- return format == FLOAT32;
- }
-
- union {
- // Index of the next uniform to write to
- // TODO: ctrulib uses 8 bits for this, however that seems to yield lots of invalid
- // indices
- // TODO: Maybe the uppermost index is for the geometry shader? Investigate!
- BitField<0, 7, u32> index;
-
- BitField<31, 1, Format> format;
- };
-
- // Writing to these registers sets the current uniform.
- u32 set_value[8];
-
- } uniform_setup;
-
- INSERT_PADDING_WORDS(0x2);
-
- struct {
- // Offset of the next instruction to write code to.
- // Incremented with each instruction write.
- u32 offset;
-
- // Writing to these registers sets the "current" word in the shader program.
- u32 set_word[8];
- } program;
-
- INSERT_PADDING_WORDS(0x1);
-
- // This register group is used to load an internal table of swizzling patterns,
- // which are indexed by each shader instruction to specify vector component swizzling.
- struct {
- // Offset of the next swizzle pattern to write code to.
- // Incremented with each instruction write.
- u32 offset;
-
- // Writing to these registers sets the current swizzle pattern in the table.
- u32 set_word[8];
- } swizzle_patterns;
-
- INSERT_PADDING_WORDS(0x2);
- };
-
- ShaderConfig gs;
- ShaderConfig vs;
-
- INSERT_PADDING_WORDS(0x20);
-
- // Map register indices to names readable by humans
- // Used for debugging purposes, so performance is not an issue here
- static std::string GetCommandName(int index);
-
- static constexpr size_t NumIds() {
- return sizeof(Regs) / sizeof(u32);
- }
-
- const u32& operator[](int index) const {
- const u32* content = reinterpret_cast<const u32*>(this);
- return content[index];
- }
-
- u32& operator[](int index) {
- u32* content = reinterpret_cast<u32*>(this);
- return content[index];
- }
-
-private:
- /*
- * Most physical addresses which Pica registers refer to are 8-byte aligned.
- * This function should be used to get the address from a raw register value.
- */
- static inline u32 DecodeAddressRegister(u32 register_value) {
- return register_value * 8;
- }
-};
-
-// TODO: MSVC does not support using offsetof() on non-static data members even though this
-// is technically allowed since C++11. This macro should be enabled once MSVC adds
-// support for that.
-#ifndef _MSC_VER
-#define ASSERT_REG_POSITION(field_name, position) \
- static_assert(offsetof(Regs, field_name) == position * 4, \
- "Field " #field_name " has invalid position")
-
-ASSERT_REG_POSITION(trigger_irq, 0x10);
-ASSERT_REG_POSITION(cull_mode, 0x40);
-ASSERT_REG_POSITION(viewport_size_x, 0x41);
-ASSERT_REG_POSITION(viewport_size_y, 0x43);
-ASSERT_REG_POSITION(viewport_depth_range, 0x4d);
-ASSERT_REG_POSITION(viewport_depth_near_plane, 0x4e);
-ASSERT_REG_POSITION(vs_output_attributes[0], 0x50);
-ASSERT_REG_POSITION(vs_output_attributes[1], 0x51);
-ASSERT_REG_POSITION(scissor_test, 0x65);
-ASSERT_REG_POSITION(viewport_corner, 0x68);
-ASSERT_REG_POSITION(depthmap_enable, 0x6D);
-ASSERT_REG_POSITION(texture0_enable, 0x80);
-ASSERT_REG_POSITION(texture0, 0x81);
-ASSERT_REG_POSITION(texture0_format, 0x8e);
-ASSERT_REG_POSITION(fragment_lighting_enable, 0x8f);
-ASSERT_REG_POSITION(texture1, 0x91);
-ASSERT_REG_POSITION(texture1_format, 0x96);
-ASSERT_REG_POSITION(texture2, 0x99);
-ASSERT_REG_POSITION(texture2_format, 0x9e);
-ASSERT_REG_POSITION(tev_stage0, 0xc0);
-ASSERT_REG_POSITION(tev_stage1, 0xc8);
-ASSERT_REG_POSITION(tev_stage2, 0xd0);
-ASSERT_REG_POSITION(tev_stage3, 0xd8);
-ASSERT_REG_POSITION(tev_combiner_buffer_input, 0xe0);
-ASSERT_REG_POSITION(fog_mode, 0xe0);
-ASSERT_REG_POSITION(fog_color, 0xe1);
-ASSERT_REG_POSITION(fog_lut_offset, 0xe6);
-ASSERT_REG_POSITION(fog_lut_data, 0xe8);
-ASSERT_REG_POSITION(tev_stage4, 0xf0);
-ASSERT_REG_POSITION(tev_stage5, 0xf8);
-ASSERT_REG_POSITION(tev_combiner_buffer_color, 0xfd);
-ASSERT_REG_POSITION(output_merger, 0x100);
-ASSERT_REG_POSITION(framebuffer, 0x110);
-ASSERT_REG_POSITION(lighting, 0x140);
-ASSERT_REG_POSITION(vertex_attributes, 0x200);
-ASSERT_REG_POSITION(index_array, 0x227);
-ASSERT_REG_POSITION(num_vertices, 0x228);
-ASSERT_REG_POSITION(vertex_offset, 0x22a);
-ASSERT_REG_POSITION(trigger_draw, 0x22e);
-ASSERT_REG_POSITION(trigger_draw_indexed, 0x22f);
-ASSERT_REG_POSITION(vs_default_attributes_setup, 0x232);
-ASSERT_REG_POSITION(command_buffer, 0x238);
-ASSERT_REG_POSITION(gpu_mode, 0x245);
-ASSERT_REG_POSITION(triangle_topology, 0x25e);
-ASSERT_REG_POSITION(restart_primitive, 0x25f);
-ASSERT_REG_POSITION(gs, 0x280);
-ASSERT_REG_POSITION(vs, 0x2b0);
-
-#undef ASSERT_REG_POSITION
-#endif // !defined(_MSC_VER)
-
-static_assert(sizeof(Regs::ShaderConfig) == 0x30 * sizeof(u32),
- "ShaderConfig structure has incorrect size");
-
-// The total number of registers is chosen arbitrarily, but let's make sure it's not some odd value
-// anyway.
-static_assert(sizeof(Regs) <= 0x300 * sizeof(u32),
- "Register set structure larger than it should be");
-static_assert(sizeof(Regs) >= 0x300 * sizeof(u32),
- "Register set structure smaller than it should be");
-
/// Initialize Pica state
void Init();
diff --git a/src/video_core/pica_state.h b/src/video_core/pica_state.h
index e4f2e6d5d..af7536d11 100644
--- a/src/video_core/pica_state.h
+++ b/src/video_core/pica_state.h
@@ -7,8 +7,8 @@
#include <array>
#include "common/bit_field.h"
#include "common/common_types.h"
-#include "video_core/pica.h"
#include "video_core/primitive_assembly.h"
+#include "video_core/regs.h"
#include "video_core/shader/shader.h"
namespace Pica {
@@ -23,7 +23,7 @@ struct State {
Shader::ShaderSetup vs;
Shader::ShaderSetup gs;
- std::array<Math::Vec4<float24>, 16> vs_default_attributes;
+ Shader::AttributeBuffer input_default_attributes;
struct {
union LutEntry {
@@ -66,7 +66,7 @@ struct State {
/// Struct used to describe immediate mode rendering state
struct ImmediateModeState {
// Used to buffer partial vertices for immediate-mode rendering.
- Shader::InputVertex input_vertex;
+ Shader::AttributeBuffer input_vertex;
// Index of the next attribute to be loaded into `input_vertex`.
u32 current_attribute = 0;
} immediate;
diff --git a/src/video_core/primitive_assembly.cpp b/src/video_core/primitive_assembly.cpp
index be7377290..acd2ac5e2 100644
--- a/src/video_core/primitive_assembly.cpp
+++ b/src/video_core/primitive_assembly.cpp
@@ -3,23 +3,23 @@
// Refer to the license.txt file included.
#include "common/logging/log.h"
-#include "video_core/pica.h"
#include "video_core/primitive_assembly.h"
+#include "video_core/regs_pipeline.h"
#include "video_core/shader/shader.h"
namespace Pica {
template <typename VertexType>
-PrimitiveAssembler<VertexType>::PrimitiveAssembler(Regs::TriangleTopology topology)
+PrimitiveAssembler<VertexType>::PrimitiveAssembler(PipelineRegs::TriangleTopology topology)
: topology(topology), buffer_index(0) {}
template <typename VertexType>
-void PrimitiveAssembler<VertexType>::SubmitVertex(VertexType& vtx,
+void PrimitiveAssembler<VertexType>::SubmitVertex(const VertexType& vtx,
TriangleHandler triangle_handler) {
switch (topology) {
// TODO: Figure out what's different with TriangleTopology::Shader.
- case Regs::TriangleTopology::List:
- case Regs::TriangleTopology::Shader:
+ case PipelineRegs::TriangleTopology::List:
+ case PipelineRegs::TriangleTopology::Shader:
if (buffer_index < 2) {
buffer[buffer_index++] = vtx;
} else {
@@ -29,8 +29,8 @@ void PrimitiveAssembler<VertexType>::SubmitVertex(VertexType& vtx,
}
break;
- case Regs::TriangleTopology::Strip:
- case Regs::TriangleTopology::Fan:
+ case PipelineRegs::TriangleTopology::Strip:
+ case PipelineRegs::TriangleTopology::Fan:
if (strip_ready)
triangle_handler(buffer[0], buffer[1], vtx);
@@ -38,9 +38,9 @@ void PrimitiveAssembler<VertexType>::SubmitVertex(VertexType& vtx,
strip_ready |= (buffer_index == 1);
- if (topology == Regs::TriangleTopology::Strip)
+ if (topology == PipelineRegs::TriangleTopology::Strip)
buffer_index = !buffer_index;
- else if (topology == Regs::TriangleTopology::Fan)
+ else if (topology == PipelineRegs::TriangleTopology::Fan)
buffer_index = 1;
break;
@@ -57,7 +57,7 @@ void PrimitiveAssembler<VertexType>::Reset() {
}
template <typename VertexType>
-void PrimitiveAssembler<VertexType>::Reconfigure(Regs::TriangleTopology topology) {
+void PrimitiveAssembler<VertexType>::Reconfigure(PipelineRegs::TriangleTopology topology) {
Reset();
this->topology = topology;
}
diff --git a/src/video_core/primitive_assembly.h b/src/video_core/primitive_assembly.h
index 0384d5984..e8eccdf27 100644
--- a/src/video_core/primitive_assembly.h
+++ b/src/video_core/primitive_assembly.h
@@ -5,7 +5,7 @@
#pragma once
#include <functional>
-#include "video_core/pica.h"
+#include "video_core/regs_pipeline.h"
namespace Pica {
@@ -15,9 +15,11 @@ namespace Pica {
*/
template <typename VertexType>
struct PrimitiveAssembler {
- using TriangleHandler = std::function<void(VertexType& v0, VertexType& v1, VertexType& v2)>;
+ using TriangleHandler =
+ std::function<void(const VertexType& v0, const VertexType& v1, const VertexType& v2)>;
- PrimitiveAssembler(Regs::TriangleTopology topology = Regs::TriangleTopology::List);
+ PrimitiveAssembler(
+ PipelineRegs::TriangleTopology topology = PipelineRegs::TriangleTopology::List);
/*
* Queues a vertex, builds primitives from the vertex queue according to the given
@@ -25,7 +27,7 @@ struct PrimitiveAssembler {
* NOTE: We could specify the triangle handler in the constructor, but this way we can
* keep event and handler code next to each other.
*/
- void SubmitVertex(VertexType& vtx, TriangleHandler triangle_handler);
+ void SubmitVertex(const VertexType& vtx, TriangleHandler triangle_handler);
/**
* Resets the internal state of the PrimitiveAssembler.
@@ -35,10 +37,10 @@ struct PrimitiveAssembler {
/**
* Reconfigures the PrimitiveAssembler to use a different triangle topology.
*/
- void Reconfigure(Regs::TriangleTopology topology);
+ void Reconfigure(PipelineRegs::TriangleTopology topology);
private:
- Regs::TriangleTopology topology;
+ PipelineRegs::TriangleTopology topology;
int buffer_index;
VertexType buffer[2];
diff --git a/src/video_core/rasterizer.h b/src/video_core/rasterizer.h
deleted file mode 100644
index 6cbda3067..000000000
--- a/src/video_core/rasterizer.h
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright 2014 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-namespace Pica {
-
-namespace Shader {
-struct OutputVertex;
-}
-
-namespace Rasterizer {
-
-void ProcessTriangle(const Shader::OutputVertex& v0, const Shader::OutputVertex& v1,
- const Shader::OutputVertex& v2);
-
-} // namespace Rasterizer
-
-} // namespace Pica
diff --git a/src/video_core/regs.cpp b/src/video_core/regs.cpp
new file mode 100644
index 000000000..2699e710a
--- /dev/null
+++ b/src/video_core/regs.cpp
@@ -0,0 +1,488 @@
+// Copyright 2015 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <iterator>
+#include <utility>
+
+#include "common/common_types.h"
+#include "video_core/regs.h"
+
+namespace Pica {
+
+static const std::pair<u16, const char*> register_names[] = {
+ {0x010, "GPUREG_FINALIZE"},
+
+ {0x040, "GPUREG_FACECULLING_CONFIG"},
+ {0x041, "GPUREG_VIEWPORT_WIDTH"},
+ {0x042, "GPUREG_VIEWPORT_INVW"},
+ {0x043, "GPUREG_VIEWPORT_HEIGHT"},
+ {0x044, "GPUREG_VIEWPORT_INVH"},
+
+ {0x047, "GPUREG_FRAGOP_CLIP"},
+ {0x048, "GPUREG_FRAGOP_CLIP_DATA0"},
+ {0x049, "GPUREG_FRAGOP_CLIP_DATA1"},
+ {0x04A, "GPUREG_FRAGOP_CLIP_DATA2"},
+ {0x04B, "GPUREG_FRAGOP_CLIP_DATA3"},
+
+ {0x04D, "GPUREG_DEPTHMAP_SCALE"},
+ {0x04E, "GPUREG_DEPTHMAP_OFFSET"},
+ {0x04F, "GPUREG_SH_OUTMAP_TOTAL"},
+ {0x050, "GPUREG_SH_OUTMAP_O0"},
+ {0x051, "GPUREG_SH_OUTMAP_O1"},
+ {0x052, "GPUREG_SH_OUTMAP_O2"},
+ {0x053, "GPUREG_SH_OUTMAP_O3"},
+ {0x054, "GPUREG_SH_OUTMAP_O4"},
+ {0x055, "GPUREG_SH_OUTMAP_O5"},
+ {0x056, "GPUREG_SH_OUTMAP_O6"},
+
+ {0x061, "GPUREG_EARLYDEPTH_FUNC"},
+ {0x062, "GPUREG_EARLYDEPTH_TEST1"},
+ {0x063, "GPUREG_EARLYDEPTH_CLEAR"},
+ {0x064, "GPUREG_SH_OUTATTR_MODE"},
+ {0x065, "GPUREG_SCISSORTEST_MODE"},
+ {0x066, "GPUREG_SCISSORTEST_POS"},
+ {0x067, "GPUREG_SCISSORTEST_DIM"},
+ {0x068, "GPUREG_VIEWPORT_XY"},
+
+ {0x06A, "GPUREG_EARLYDEPTH_DATA"},
+
+ {0x06D, "GPUREG_DEPTHMAP_ENABLE"},
+ {0x06E, "GPUREG_RENDERBUF_DIM"},
+ {0x06F, "GPUREG_SH_OUTATTR_CLOCK"},
+
+ {0x080, "GPUREG_TEXUNIT_CONFIG"},
+ {0x081, "GPUREG_TEXUNIT0_BORDER_COLOR"},
+ {0x082, "GPUREG_TEXUNIT0_DIM"},
+ {0x083, "GPUREG_TEXUNIT0_PARAM"},
+ {0x084, "GPUREG_TEXUNIT0_LOD"},
+ {0x085, "GPUREG_TEXUNIT0_ADDR1"},
+ {0x086, "GPUREG_TEXUNIT0_ADDR2"},
+ {0x087, "GPUREG_TEXUNIT0_ADDR3"},
+ {0x088, "GPUREG_TEXUNIT0_ADDR4"},
+ {0x089, "GPUREG_TEXUNIT0_ADDR5"},
+ {0x08A, "GPUREG_TEXUNIT0_ADDR6"},
+ {0x08B, "GPUREG_TEXUNIT0_SHADOW"},
+
+ {0x08E, "GPUREG_TEXUNIT0_TYPE"},
+ {0x08F, "GPUREG_LIGHTING_ENABLE0"},
+
+ {0x091, "GPUREG_TEXUNIT1_BORDER_COLOR"},
+ {0x092, "GPUREG_TEXUNIT1_DIM"},
+ {0x093, "GPUREG_TEXUNIT1_PARAM"},
+ {0x094, "GPUREG_TEXUNIT1_LOD"},
+ {0x095, "GPUREG_TEXUNIT1_ADDR"},
+ {0x096, "GPUREG_TEXUNIT1_TYPE"},
+
+ {0x099, "GPUREG_TEXUNIT2_BORDER_COLOR"},
+ {0x09A, "GPUREG_TEXUNIT2_DIM"},
+ {0x09B, "GPUREG_TEXUNIT2_PARAM"},
+ {0x09C, "GPUREG_TEXUNIT2_LOD"},
+ {0x09D, "GPUREG_TEXUNIT2_ADDR"},
+ {0x09E, "GPUREG_TEXUNIT2_TYPE"},
+
+ {0x0A8, "GPUREG_TEXUNIT3_PROCTEX0"},
+ {0x0A9, "GPUREG_TEXUNIT3_PROCTEX1"},
+ {0x0AA, "GPUREG_TEXUNIT3_PROCTEX2"},
+ {0x0AB, "GPUREG_TEXUNIT3_PROCTEX3"},
+ {0x0AC, "GPUREG_TEXUNIT3_PROCTEX4"},
+ {0x0AD, "GPUREG_TEXUNIT3_PROCTEX5"},
+
+ {0x0AF, "GPUREG_PROCTEX_LUT"},
+ {0x0B0, "GPUREG_PROCTEX_LUT_DATA0"},
+ {0x0B1, "GPUREG_PROCTEX_LUT_DATA1"},
+ {0x0B2, "GPUREG_PROCTEX_LUT_DATA2"},
+ {0x0B3, "GPUREG_PROCTEX_LUT_DATA3"},
+ {0x0B4, "GPUREG_PROCTEX_LUT_DATA4"},
+ {0x0B5, "GPUREG_PROCTEX_LUT_DATA5"},
+ {0x0B6, "GPUREG_PROCTEX_LUT_DATA6"},
+ {0x0B7, "GPUREG_PROCTEX_LUT_DATA7"},
+
+ {0x0C0, "GPUREG_TEXENV0_SOURCE"},
+ {0x0C1, "GPUREG_TEXENV0_OPERAND"},
+ {0x0C2, "GPUREG_TEXENV0_COMBINER"},
+ {0x0C3, "GPUREG_TEXENV0_COLOR"},
+ {0x0C4, "GPUREG_TEXENV0_SCALE"},
+
+ {0x0C8, "GPUREG_TEXENV1_SOURCE"},
+ {0x0C9, "GPUREG_TEXENV1_OPERAND"},
+ {0x0CA, "GPUREG_TEXENV1_COMBINER"},
+ {0x0CB, "GPUREG_TEXENV1_COLOR"},
+ {0x0CC, "GPUREG_TEXENV1_SCALE"},
+
+ {0x0D0, "GPUREG_TEXENV2_SOURCE"},
+ {0x0D1, "GPUREG_TEXENV2_OPERAND"},
+ {0x0D2, "GPUREG_TEXENV2_COMBINER"},
+ {0x0D3, "GPUREG_TEXENV2_COLOR"},
+ {0x0D4, "GPUREG_TEXENV2_SCALE"},
+
+ {0x0D8, "GPUREG_TEXENV3_SOURCE"},
+ {0x0D9, "GPUREG_TEXENV3_OPERAND"},
+ {0x0DA, "GPUREG_TEXENV3_COMBINER"},
+ {0x0DB, "GPUREG_TEXENV3_COLOR"},
+ {0x0DC, "GPUREG_TEXENV3_SCALE"},
+
+ {0x0E0, "GPUREG_TEXENV_UPDATE_BUFFER"},
+ {0x0E1, "GPUREG_FOG_COLOR"},
+
+ {0x0E4, "GPUREG_GAS_ATTENUATION"},
+ {0x0E5, "GPUREG_GAS_ACCMAX"},
+ {0x0E6, "GPUREG_FOG_LUT_INDEX"},
+
+ {0x0E8, "GPUREG_FOG_LUT_DATA0"},
+ {0x0E9, "GPUREG_FOG_LUT_DATA1"},
+ {0x0EA, "GPUREG_FOG_LUT_DATA2"},
+ {0x0EB, "GPUREG_FOG_LUT_DATA3"},
+ {0x0EC, "GPUREG_FOG_LUT_DATA4"},
+ {0x0ED, "GPUREG_FOG_LUT_DATA5"},
+ {0x0EE, "GPUREG_FOG_LUT_DATA6"},
+ {0x0EF, "GPUREG_FOG_LUT_DATA7"},
+ {0x0F0, "GPUREG_TEXENV4_SOURCE"},
+ {0x0F1, "GPUREG_TEXENV4_OPERAND"},
+ {0x0F2, "GPUREG_TEXENV4_COMBINER"},
+ {0x0F3, "GPUREG_TEXENV4_COLOR"},
+ {0x0F4, "GPUREG_TEXENV4_SCALE"},
+
+ {0x0F8, "GPUREG_TEXENV5_SOURCE"},
+ {0x0F9, "GPUREG_TEXENV5_OPERAND"},
+ {0x0FA, "GPUREG_TEXENV5_COMBINER"},
+ {0x0FB, "GPUREG_TEXENV5_COLOR"},
+ {0x0FC, "GPUREG_TEXENV5_SCALE"},
+ {0x0FD, "GPUREG_TEXENV_BUFFER_COLOR"},
+
+ {0x100, "GPUREG_COLOR_OPERATION"},
+ {0x101, "GPUREG_BLEND_FUNC"},
+ {0x102, "GPUREG_LOGIC_OP"},
+ {0x103, "GPUREG_BLEND_COLOR"},
+ {0x104, "GPUREG_FRAGOP_ALPHA_TEST"},
+ {0x105, "GPUREG_STENCIL_TEST"},
+ {0x106, "GPUREG_STENCIL_OP"},
+ {0x107, "GPUREG_DEPTH_COLOR_MASK"},
+
+ {0x110, "GPUREG_FRAMEBUFFER_INVALIDATE"},
+ {0x111, "GPUREG_FRAMEBUFFER_FLUSH"},
+ {0x112, "GPUREG_COLORBUFFER_READ"},
+ {0x113, "GPUREG_COLORBUFFER_WRITE"},
+ {0x114, "GPUREG_DEPTHBUFFER_READ"},
+ {0x115, "GPUREG_DEPTHBUFFER_WRITE"},
+ {0x116, "GPUREG_DEPTHBUFFER_FORMAT"},
+ {0x117, "GPUREG_COLORBUFFER_FORMAT"},
+ {0x118, "GPUREG_EARLYDEPTH_TEST2"},
+
+ {0x11B, "GPUREG_FRAMEBUFFER_BLOCK32"},
+ {0x11C, "GPUREG_DEPTHBUFFER_LOC"},
+ {0x11D, "GPUREG_COLORBUFFER_LOC"},
+ {0x11E, "GPUREG_FRAMEBUFFER_DIM"},
+
+ {0x120, "GPUREG_GAS_LIGHT_XY"},
+ {0x121, "GPUREG_GAS_LIGHT_Z"},
+ {0x122, "GPUREG_GAS_LIGHT_Z_COLOR"},
+ {0x123, "GPUREG_GAS_LUT_INDEX"},
+ {0x124, "GPUREG_GAS_LUT_DATA"},
+
+ {0x126, "GPUREG_GAS_DELTAZ_DEPTH"},
+
+ {0x130, "GPUREG_FRAGOP_SHADOW"},
+
+ {0x140, "GPUREG_LIGHT0_SPECULAR0"},
+ {0x141, "GPUREG_LIGHT0_SPECULAR1"},
+ {0x142, "GPUREG_LIGHT0_DIFFUSE"},
+ {0x143, "GPUREG_LIGHT0_AMBIENT"},
+ {0x144, "GPUREG_LIGHT0_XY"},
+ {0x145, "GPUREG_LIGHT0_Z"},
+ {0x146, "GPUREG_LIGHT0_SPOTDIR_XY"},
+ {0x147, "GPUREG_LIGHT0_SPOTDIR_Z"},
+
+ {0x149, "GPUREG_LIGHT0_CONFIG"},
+ {0x14A, "GPUREG_LIGHT0_ATTENUATION_BIAS"},
+ {0x14B, "GPUREG_LIGHT0_ATTENUATION_SCALE"},
+
+ {0x150, "GPUREG_LIGHT1_SPECULAR0"},
+ {0x151, "GPUREG_LIGHT1_SPECULAR1"},
+ {0x152, "GPUREG_LIGHT1_DIFFUSE"},
+ {0x153, "GPUREG_LIGHT1_AMBIENT"},
+ {0x154, "GPUREG_LIGHT1_XY"},
+ {0x155, "GPUREG_LIGHT1_Z"},
+ {0x156, "GPUREG_LIGHT1_SPOTDIR_XY"},
+ {0x157, "GPUREG_LIGHT1_SPOTDIR_Z"},
+
+ {0x159, "GPUREG_LIGHT1_CONFIG"},
+ {0x15A, "GPUREG_LIGHT1_ATTENUATION_BIAS"},
+ {0x15B, "GPUREG_LIGHT1_ATTENUATION_SCALE"},
+
+ {0x160, "GPUREG_LIGHT2_SPECULAR0"},
+ {0x161, "GPUREG_LIGHT2_SPECULAR1"},
+ {0x162, "GPUREG_LIGHT2_DIFFUSE"},
+ {0x163, "GPUREG_LIGHT2_AMBIENT"},
+ {0x164, "GPUREG_LIGHT2_XY"},
+ {0x165, "GPUREG_LIGHT2_Z"},
+ {0x166, "GPUREG_LIGHT2_SPOTDIR_XY"},
+ {0x167, "GPUREG_LIGHT2_SPOTDIR_Z"},
+
+ {0x169, "GPUREG_LIGHT2_CONFIG"},
+ {0x16A, "GPUREG_LIGHT2_ATTENUATION_BIAS"},
+ {0x16B, "GPUREG_LIGHT2_ATTENUATION_SCALE"},
+
+ {0x170, "GPUREG_LIGHT3_SPECULAR0"},
+ {0x171, "GPUREG_LIGHT3_SPECULAR1"},
+ {0x172, "GPUREG_LIGHT3_DIFFUSE"},
+ {0x173, "GPUREG_LIGHT3_AMBIENT"},
+ {0x174, "GPUREG_LIGHT3_XY"},
+ {0x175, "GPUREG_LIGHT3_Z"},
+ {0x176, "GPUREG_LIGHT3_SPOTDIR_XY"},
+ {0x177, "GPUREG_LIGHT3_SPOTDIR_Z"},
+
+ {0x179, "GPUREG_LIGHT3_CONFIG"},
+ {0x17A, "GPUREG_LIGHT3_ATTENUATION_BIAS"},
+ {0x17B, "GPUREG_LIGHT3_ATTENUATION_SCALE"},
+
+ {0x180, "GPUREG_LIGHT4_SPECULAR0"},
+ {0x181, "GPUREG_LIGHT4_SPECULAR1"},
+ {0x182, "GPUREG_LIGHT4_DIFFUSE"},
+ {0x183, "GPUREG_LIGHT4_AMBIENT"},
+ {0x184, "GPUREG_LIGHT4_XY"},
+ {0x185, "GPUREG_LIGHT4_Z"},
+ {0x186, "GPUREG_LIGHT4_SPOTDIR_XY"},
+ {0x187, "GPUREG_LIGHT4_SPOTDIR_Z"},
+
+ {0x189, "GPUREG_LIGHT4_CONFIG"},
+ {0x18A, "GPUREG_LIGHT4_ATTENUATION_BIAS"},
+ {0x18B, "GPUREG_LIGHT4_ATTENUATION_SCALE"},
+
+ {0x190, "GPUREG_LIGHT5_SPECULAR0"},
+ {0x191, "GPUREG_LIGHT5_SPECULAR1"},
+ {0x192, "GPUREG_LIGHT5_DIFFUSE"},
+ {0x193, "GPUREG_LIGHT5_AMBIENT"},
+ {0x194, "GPUREG_LIGHT5_XY"},
+ {0x195, "GPUREG_LIGHT5_Z"},
+ {0x196, "GPUREG_LIGHT5_SPOTDIR_XY"},
+ {0x197, "GPUREG_LIGHT5_SPOTDIR_Z"},
+
+ {0x199, "GPUREG_LIGHT5_CONFIG"},
+ {0x19A, "GPUREG_LIGHT5_ATTENUATION_BIAS"},
+ {0x19B, "GPUREG_LIGHT5_ATTENUATION_SCALE"},
+
+ {0x1A0, "GPUREG_LIGHT6_SPECULAR0"},
+ {0x1A1, "GPUREG_LIGHT6_SPECULAR1"},
+ {0x1A2, "GPUREG_LIGHT6_DIFFUSE"},
+ {0x1A3, "GPUREG_LIGHT6_AMBIENT"},
+ {0x1A4, "GPUREG_LIGHT6_XY"},
+ {0x1A5, "GPUREG_LIGHT6_Z"},
+ {0x1A6, "GPUREG_LIGHT6_SPOTDIR_XY"},
+ {0x1A7, "GPUREG_LIGHT6_SPOTDIR_Z"},
+
+ {0x1A9, "GPUREG_LIGHT6_CONFIG"},
+ {0x1AA, "GPUREG_LIGHT6_ATTENUATION_BIAS"},
+ {0x1AB, "GPUREG_LIGHT6_ATTENUATION_SCALE"},
+
+ {0x1B0, "GPUREG_LIGHT7_SPECULAR0"},
+ {0x1B1, "GPUREG_LIGHT7_SPECULAR1"},
+ {0x1B2, "GPUREG_LIGHT7_DIFFUSE"},
+ {0x1B3, "GPUREG_LIGHT7_AMBIENT"},
+ {0x1B4, "GPUREG_LIGHT7_XY"},
+ {0x1B5, "GPUREG_LIGHT7_Z"},
+ {0x1B6, "GPUREG_LIGHT7_SPOTDIR_XY"},
+ {0x1B7, "GPUREG_LIGHT7_SPOTDIR_Z"},
+
+ {0x1B9, "GPUREG_LIGHT7_CONFIG"},
+ {0x1BA, "GPUREG_LIGHT7_ATTENUATION_BIAS"},
+ {0x1BB, "GPUREG_LIGHT7_ATTENUATION_SCALE"},
+
+ {0x1C0, "GPUREG_LIGHTING_AMBIENT"},
+
+ {0x1C2, "GPUREG_LIGHTING_NUM_LIGHTS"},
+ {0x1C3, "GPUREG_LIGHTING_CONFIG0"},
+ {0x1C4, "GPUREG_LIGHTING_CONFIG1"},
+ {0x1C5, "GPUREG_LIGHTING_LUT_INDEX"},
+ {0x1C6, "GPUREG_LIGHTING_ENABLE1"},
+
+ {0x1C8, "GPUREG_LIGHTING_LUT_DATA0"},
+ {0x1C9, "GPUREG_LIGHTING_LUT_DATA1"},
+ {0x1CA, "GPUREG_LIGHTING_LUT_DATA2"},
+ {0x1CB, "GPUREG_LIGHTING_LUT_DATA3"},
+ {0x1CC, "GPUREG_LIGHTING_LUT_DATA4"},
+ {0x1CD, "GPUREG_LIGHTING_LUT_DATA5"},
+ {0x1CE, "GPUREG_LIGHTING_LUT_DATA6"},
+ {0x1CF, "GPUREG_LIGHTING_LUT_DATA7"},
+ {0x1D0, "GPUREG_LIGHTING_LUTINPUT_ABS"},
+ {0x1D1, "GPUREG_LIGHTING_LUTINPUT_SELECT"},
+ {0x1D2, "GPUREG_LIGHTING_LUTINPUT_SCALE"},
+
+ {0x1D9, "GPUREG_LIGHTING_LIGHT_PERMUTATION"},
+
+ {0x200, "GPUREG_ATTRIBBUFFERS_LOC"},
+ {0x201, "GPUREG_ATTRIBBUFFERS_FORMAT_LOW"},
+ {0x202, "GPUREG_ATTRIBBUFFERS_FORMAT_HIGH"},
+ {0x203, "GPUREG_ATTRIBBUFFER0_OFFSET"},
+ {0x204, "GPUREG_ATTRIBBUFFER0_CONFIG1"},
+ {0x205, "GPUREG_ATTRIBBUFFER0_CONFIG2"},
+ {0x206, "GPUREG_ATTRIBBUFFER1_OFFSET"},
+ {0x207, "GPUREG_ATTRIBBUFFER1_CONFIG1"},
+ {0x208, "GPUREG_ATTRIBBUFFER1_CONFIG2"},
+ {0x209, "GPUREG_ATTRIBBUFFER2_OFFSET"},
+ {0x20A, "GPUREG_ATTRIBBUFFER2_CONFIG1"},
+ {0x20B, "GPUREG_ATTRIBBUFFER2_CONFIG2"},
+ {0x20C, "GPUREG_ATTRIBBUFFER3_OFFSET"},
+ {0x20D, "GPUREG_ATTRIBBUFFER3_CONFIG1"},
+ {0x20E, "GPUREG_ATTRIBBUFFER3_CONFIG2"},
+ {0x20F, "GPUREG_ATTRIBBUFFER4_OFFSET"},
+ {0x210, "GPUREG_ATTRIBBUFFER4_CONFIG1"},
+ {0x211, "GPUREG_ATTRIBBUFFER4_CONFIG2"},
+ {0x212, "GPUREG_ATTRIBBUFFER5_OFFSET"},
+ {0x213, "GPUREG_ATTRIBBUFFER5_CONFIG1"},
+ {0x214, "GPUREG_ATTRIBBUFFER5_CONFIG2"},
+ {0x215, "GPUREG_ATTRIBBUFFER6_OFFSET"},
+ {0x216, "GPUREG_ATTRIBBUFFER6_CONFIG1"},
+ {0x217, "GPUREG_ATTRIBBUFFER6_CONFIG2"},
+ {0x218, "GPUREG_ATTRIBBUFFER7_OFFSET"},
+ {0x219, "GPUREG_ATTRIBBUFFER7_CONFIG1"},
+ {0x21A, "GPUREG_ATTRIBBUFFER7_CONFIG2"},
+ {0x21B, "GPUREG_ATTRIBBUFFER8_OFFSET"},
+ {0x21C, "GPUREG_ATTRIBBUFFER8_CONFIG1"},
+ {0x21D, "GPUREG_ATTRIBBUFFER8_CONFIG2"},
+ {0x21E, "GPUREG_ATTRIBBUFFER9_OFFSET"},
+ {0x21F, "GPUREG_ATTRIBBUFFER9_CONFIG1"},
+ {0x220, "GPUREG_ATTRIBBUFFER9_CONFIG2"},
+ {0x221, "GPUREG_ATTRIBBUFFER10_OFFSET"},
+ {0x222, "GPUREG_ATTRIBBUFFER10_CONFIG1"},
+ {0x223, "GPUREG_ATTRIBBUFFER10_CONFIG2"},
+ {0x224, "GPUREG_ATTRIBBUFFER11_OFFSET"},
+ {0x225, "GPUREG_ATTRIBBUFFER11_CONFIG1"},
+ {0x226, "GPUREG_ATTRIBBUFFER11_CONFIG2"},
+ {0x227, "GPUREG_INDEXBUFFER_CONFIG"},
+ {0x228, "GPUREG_NUMVERTICES"},
+ {0x229, "GPUREG_GEOSTAGE_CONFIG"},
+ {0x22A, "GPUREG_VERTEX_OFFSET"},
+
+ {0x22D, "GPUREG_POST_VERTEX_CACHE_NUM"},
+ {0x22E, "GPUREG_DRAWARRAYS"},
+ {0x22F, "GPUREG_DRAWELEMENTS"},
+
+ {0x231, "GPUREG_VTX_FUNC"},
+ {0x232, "GPUREG_FIXEDATTRIB_INDEX"},
+ {0x233, "GPUREG_FIXEDATTRIB_DATA0"},
+ {0x234, "GPUREG_FIXEDATTRIB_DATA1"},
+ {0x235, "GPUREG_FIXEDATTRIB_DATA2"},
+
+ {0x238, "GPUREG_CMDBUF_SIZE0"},
+ {0x239, "GPUREG_CMDBUF_SIZE1"},
+ {0x23A, "GPUREG_CMDBUF_ADDR0"},
+ {0x23B, "GPUREG_CMDBUF_ADDR1"},
+ {0x23C, "GPUREG_CMDBUF_JUMP0"},
+ {0x23D, "GPUREG_CMDBUF_JUMP1"},
+
+ {0x242, "GPUREG_VSH_NUM_ATTR"},
+
+ {0x244, "GPUREG_VSH_COM_MODE"},
+ {0x245, "GPUREG_START_DRAW_FUNC0"},
+
+ {0x24A, "GPUREG_VSH_OUTMAP_TOTAL1"},
+
+ {0x251, "GPUREG_VSH_OUTMAP_TOTAL2"},
+ {0x252, "GPUREG_GSH_MISC0"},
+ {0x253, "GPUREG_GEOSTAGE_CONFIG2"},
+ {0x254, "GPUREG_GSH_MISC1"},
+
+ {0x25E, "GPUREG_PRIMITIVE_CONFIG"},
+ {0x25F, "GPUREG_RESTART_PRIMITIVE"},
+
+ {0x280, "GPUREG_GSH_BOOLUNIFORM"},
+ {0x281, "GPUREG_GSH_INTUNIFORM_I0"},
+ {0x282, "GPUREG_GSH_INTUNIFORM_I1"},
+ {0x283, "GPUREG_GSH_INTUNIFORM_I2"},
+ {0x284, "GPUREG_GSH_INTUNIFORM_I3"},
+
+ {0x289, "GPUREG_GSH_INPUTBUFFER_CONFIG"},
+ {0x28A, "GPUREG_GSH_ENTRYPOINT"},
+ {0x28B, "GPUREG_GSH_ATTRIBUTES_PERMUTATION_LOW"},
+ {0x28C, "GPUREG_GSH_ATTRIBUTES_PERMUTATION_HIGH"},
+ {0x28D, "GPUREG_GSH_OUTMAP_MASK"},
+
+ {0x28F, "GPUREG_GSH_CODETRANSFER_END"},
+ {0x290, "GPUREG_GSH_FLOATUNIFORM_INDEX"},
+ {0x291, "GPUREG_GSH_FLOATUNIFORM_DATA0"},
+ {0x292, "GPUREG_GSH_FLOATUNIFORM_DATA1"},
+ {0x293, "GPUREG_GSH_FLOATUNIFORM_DATA2"},
+ {0x294, "GPUREG_GSH_FLOATUNIFORM_DATA3"},
+ {0x295, "GPUREG_GSH_FLOATUNIFORM_DATA4"},
+ {0x296, "GPUREG_GSH_FLOATUNIFORM_DATA5"},
+ {0x297, "GPUREG_GSH_FLOATUNIFORM_DATA6"},
+ {0x298, "GPUREG_GSH_FLOATUNIFORM_DATA7"},
+
+ {0x29B, "GPUREG_GSH_CODETRANSFER_INDEX"},
+ {0x29C, "GPUREG_GSH_CODETRANSFER_DATA0"},
+ {0x29D, "GPUREG_GSH_CODETRANSFER_DATA1"},
+ {0x29E, "GPUREG_GSH_CODETRANSFER_DATA2"},
+ {0x29F, "GPUREG_GSH_CODETRANSFER_DATA3"},
+ {0x2A0, "GPUREG_GSH_CODETRANSFER_DATA4"},
+ {0x2A1, "GPUREG_GSH_CODETRANSFER_DATA5"},
+ {0x2A2, "GPUREG_GSH_CODETRANSFER_DATA6"},
+ {0x2A3, "GPUREG_GSH_CODETRANSFER_DATA7"},
+
+ {0x2A5, "GPUREG_GSH_OPDESCS_INDEX"},
+ {0x2A6, "GPUREG_GSH_OPDESCS_DATA0"},
+ {0x2A7, "GPUREG_GSH_OPDESCS_DATA1"},
+ {0x2A8, "GPUREG_GSH_OPDESCS_DATA2"},
+ {0x2A9, "GPUREG_GSH_OPDESCS_DATA3"},
+ {0x2AA, "GPUREG_GSH_OPDESCS_DATA4"},
+ {0x2AB, "GPUREG_GSH_OPDESCS_DATA5"},
+ {0x2AC, "GPUREG_GSH_OPDESCS_DATA6"},
+ {0x2AD, "GPUREG_GSH_OPDESCS_DATA7"},
+
+ {0x2B0, "GPUREG_VSH_BOOLUNIFORM"},
+ {0x2B1, "GPUREG_VSH_INTUNIFORM_I0"},
+ {0x2B2, "GPUREG_VSH_INTUNIFORM_I1"},
+ {0x2B3, "GPUREG_VSH_INTUNIFORM_I2"},
+ {0x2B4, "GPUREG_VSH_INTUNIFORM_I3"},
+
+ {0x2B9, "GPUREG_VSH_INPUTBUFFER_CONFIG"},
+ {0x2BA, "GPUREG_VSH_ENTRYPOINT"},
+ {0x2BB, "GPUREG_VSH_ATTRIBUTES_PERMUTATION_LOW"},
+ {0x2BC, "GPUREG_VSH_ATTRIBUTES_PERMUTATION_HIGH"},
+ {0x2BD, "GPUREG_VSH_OUTMAP_MASK"},
+
+ {0x2BF, "GPUREG_VSH_CODETRANSFER_END"},
+ {0x2C0, "GPUREG_VSH_FLOATUNIFORM_INDEX"},
+ {0x2C1, "GPUREG_VSH_FLOATUNIFORM_DATA0"},
+ {0x2C2, "GPUREG_VSH_FLOATUNIFORM_DATA1"},
+ {0x2C3, "GPUREG_VSH_FLOATUNIFORM_DATA2"},
+ {0x2C4, "GPUREG_VSH_FLOATUNIFORM_DATA3"},
+ {0x2C5, "GPUREG_VSH_FLOATUNIFORM_DATA4"},
+ {0x2C6, "GPUREG_VSH_FLOATUNIFORM_DATA5"},
+ {0x2C7, "GPUREG_VSH_FLOATUNIFORM_DATA6"},
+ {0x2C8, "GPUREG_VSH_FLOATUNIFORM_DATA7"},
+
+ {0x2CB, "GPUREG_VSH_CODETRANSFER_INDEX"},
+ {0x2CC, "GPUREG_VSH_CODETRANSFER_DATA0"},
+ {0x2CD, "GPUREG_VSH_CODETRANSFER_DATA1"},
+ {0x2CE, "GPUREG_VSH_CODETRANSFER_DATA2"},
+ {0x2CF, "GPUREG_VSH_CODETRANSFER_DATA3"},
+ {0x2D0, "GPUREG_VSH_CODETRANSFER_DATA4"},
+ {0x2D1, "GPUREG_VSH_CODETRANSFER_DATA5"},
+ {0x2D2, "GPUREG_VSH_CODETRANSFER_DATA6"},
+ {0x2D3, "GPUREG_VSH_CODETRANSFER_DATA7"},
+
+ {0x2D5, "GPUREG_VSH_OPDESCS_INDEX"},
+ {0x2D6, "GPUREG_VSH_OPDESCS_DATA0"},
+ {0x2D7, "GPUREG_VSH_OPDESCS_DATA1"},
+ {0x2D8, "GPUREG_VSH_OPDESCS_DATA2"},
+ {0x2D9, "GPUREG_VSH_OPDESCS_DATA3"},
+ {0x2DA, "GPUREG_VSH_OPDESCS_DATA4"},
+ {0x2DB, "GPUREG_VSH_OPDESCS_DATA5"},
+ {0x2DC, "GPUREG_VSH_OPDESCS_DATA6"},
+ {0x2DD, "GPUREG_VSH_OPDESCS_DATA7"},
+};
+
+const char* Regs::GetRegisterName(u16 index) {
+ auto found = std::lower_bound(std::begin(register_names), std::end(register_names), index,
+ [](auto p, auto i) { return p.first < i; });
+ if (found->first == index) {
+ return found->second;
+ } else {
+ // Return empty string if no match is found
+ return "";
+ }
+}
+
+} // namespace Pica
diff --git a/src/video_core/regs.h b/src/video_core/regs.h
new file mode 100644
index 000000000..86826088b
--- /dev/null
+++ b/src/video_core/regs.h
@@ -0,0 +1,142 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <cstddef>
+#include <string>
+#ifndef _MSC_VER
+#include <type_traits> // for std::enable_if
+#endif
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "video_core/regs_framebuffer.h"
+#include "video_core/regs_lighting.h"
+#include "video_core/regs_pipeline.h"
+#include "video_core/regs_rasterizer.h"
+#include "video_core/regs_shader.h"
+#include "video_core/regs_texturing.h"
+
+namespace Pica {
+
+// Returns index corresponding to the Regs member labeled by field_name
+// TODO: Due to Visual studio bug 209229, offsetof does not return constant expressions
+// when used with array elements (e.g. PICA_REG_INDEX(vs_uniform_setup.set_value[1])).
+// For details cf.
+// https://connect.microsoft.com/VisualStudio/feedback/details/209229/offsetof-does-not-produce-a-constant-expression-for-array-members
+// Hopefully, this will be fixed sometime in the future.
+// For lack of better alternatives, we currently hardcode the offsets when constant
+// expressions are needed via PICA_REG_INDEX_WORKAROUND (on sane compilers, static_asserts
+// will then make sure the offsets indeed match the automatically calculated ones).
+#define PICA_REG_INDEX(field_name) (offsetof(Pica::Regs, field_name) / sizeof(u32))
+#if defined(_MSC_VER)
+#define PICA_REG_INDEX_WORKAROUND(field_name, backup_workaround_index) (backup_workaround_index)
+#else
+// NOTE: Yeah, hacking in a static_assert here just to workaround the lacking MSVC compiler
+// really is this annoying. This macro just forwards its first argument to PICA_REG_INDEX
+// and then performs a (no-op) cast to size_t iff the second argument matches the expected
+// field offset. Otherwise, the compiler will fail to compile this code.
+#define PICA_REG_INDEX_WORKAROUND(field_name, backup_workaround_index) \
+ ((typename std::enable_if<backup_workaround_index == PICA_REG_INDEX(field_name), \
+ size_t>::type)PICA_REG_INDEX(field_name))
+#endif // _MSC_VER
+
+struct Regs {
+ static constexpr size_t NUM_REGS = 0x300;
+
+ union {
+ struct {
+ INSERT_PADDING_WORDS(0x10);
+ u32 trigger_irq;
+ INSERT_PADDING_WORDS(0x2f);
+ RasterizerRegs rasterizer;
+ TexturingRegs texturing;
+ FramebufferRegs framebuffer;
+ LightingRegs lighting;
+ PipelineRegs pipeline;
+ ShaderRegs gs;
+ ShaderRegs vs;
+ INSERT_PADDING_WORDS(0x20);
+ };
+ std::array<u32, NUM_REGS> reg_array;
+ };
+
+ /// Map register indices to names readable by humans
+ static const char* GetRegisterName(u16 index);
+};
+
+static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), "Regs struct has wrong size");
+
+// TODO: MSVC does not support using offsetof() on non-static data members even though this
+// is technically allowed since C++11. This macro should be enabled once MSVC adds
+// support for that.
+#ifndef _MSC_VER
+#define ASSERT_REG_POSITION(field_name, position) \
+ static_assert(offsetof(Regs, field_name) == position * 4, \
+ "Field " #field_name " has invalid position")
+
+ASSERT_REG_POSITION(trigger_irq, 0x10);
+
+ASSERT_REG_POSITION(rasterizer, 0x40);
+ASSERT_REG_POSITION(rasterizer.cull_mode, 0x40);
+ASSERT_REG_POSITION(rasterizer.viewport_size_x, 0x41);
+ASSERT_REG_POSITION(rasterizer.viewport_size_y, 0x43);
+ASSERT_REG_POSITION(rasterizer.viewport_depth_range, 0x4d);
+ASSERT_REG_POSITION(rasterizer.viewport_depth_near_plane, 0x4e);
+ASSERT_REG_POSITION(rasterizer.vs_output_attributes[0], 0x50);
+ASSERT_REG_POSITION(rasterizer.vs_output_attributes[1], 0x51);
+ASSERT_REG_POSITION(rasterizer.scissor_test, 0x65);
+ASSERT_REG_POSITION(rasterizer.viewport_corner, 0x68);
+ASSERT_REG_POSITION(rasterizer.depthmap_enable, 0x6D);
+
+ASSERT_REG_POSITION(texturing, 0x80);
+ASSERT_REG_POSITION(texturing.texture0_enable, 0x80);
+ASSERT_REG_POSITION(texturing.texture0, 0x81);
+ASSERT_REG_POSITION(texturing.texture0_format, 0x8e);
+ASSERT_REG_POSITION(texturing.fragment_lighting_enable, 0x8f);
+ASSERT_REG_POSITION(texturing.texture1, 0x91);
+ASSERT_REG_POSITION(texturing.texture1_format, 0x96);
+ASSERT_REG_POSITION(texturing.texture2, 0x99);
+ASSERT_REG_POSITION(texturing.texture2_format, 0x9e);
+ASSERT_REG_POSITION(texturing.tev_stage0, 0xc0);
+ASSERT_REG_POSITION(texturing.tev_stage1, 0xc8);
+ASSERT_REG_POSITION(texturing.tev_stage2, 0xd0);
+ASSERT_REG_POSITION(texturing.tev_stage3, 0xd8);
+ASSERT_REG_POSITION(texturing.tev_combiner_buffer_input, 0xe0);
+ASSERT_REG_POSITION(texturing.fog_mode, 0xe0);
+ASSERT_REG_POSITION(texturing.fog_color, 0xe1);
+ASSERT_REG_POSITION(texturing.fog_lut_offset, 0xe6);
+ASSERT_REG_POSITION(texturing.fog_lut_data, 0xe8);
+ASSERT_REG_POSITION(texturing.tev_stage4, 0xf0);
+ASSERT_REG_POSITION(texturing.tev_stage5, 0xf8);
+ASSERT_REG_POSITION(texturing.tev_combiner_buffer_color, 0xfd);
+
+ASSERT_REG_POSITION(framebuffer, 0x100);
+ASSERT_REG_POSITION(framebuffer.output_merger, 0x100);
+ASSERT_REG_POSITION(framebuffer.framebuffer, 0x110);
+
+ASSERT_REG_POSITION(lighting, 0x140);
+
+ASSERT_REG_POSITION(pipeline, 0x200);
+ASSERT_REG_POSITION(pipeline.vertex_attributes, 0x200);
+ASSERT_REG_POSITION(pipeline.index_array, 0x227);
+ASSERT_REG_POSITION(pipeline.num_vertices, 0x228);
+ASSERT_REG_POSITION(pipeline.vertex_offset, 0x22a);
+ASSERT_REG_POSITION(pipeline.trigger_draw, 0x22e);
+ASSERT_REG_POSITION(pipeline.trigger_draw_indexed, 0x22f);
+ASSERT_REG_POSITION(pipeline.vs_default_attributes_setup, 0x232);
+ASSERT_REG_POSITION(pipeline.command_buffer, 0x238);
+ASSERT_REG_POSITION(pipeline.gpu_mode, 0x245);
+ASSERT_REG_POSITION(pipeline.triangle_topology, 0x25e);
+ASSERT_REG_POSITION(pipeline.restart_primitive, 0x25f);
+
+ASSERT_REG_POSITION(gs, 0x280);
+ASSERT_REG_POSITION(vs, 0x2b0);
+
+#undef ASSERT_REG_POSITION
+#endif // !defined(_MSC_VER)
+
+} // namespace Pica
diff --git a/src/video_core/regs_framebuffer.h b/src/video_core/regs_framebuffer.h
new file mode 100644
index 000000000..9ddc79243
--- /dev/null
+++ b/src/video_core/regs_framebuffer.h
@@ -0,0 +1,284 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+
+#include "common/assert.h"
+#include "common/bit_field.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/logging/log.h"
+
+namespace Pica {
+
+struct FramebufferRegs {
+ enum class LogicOp : u32 {
+ Clear = 0,
+ And = 1,
+ AndReverse = 2,
+ Copy = 3,
+ Set = 4,
+ CopyInverted = 5,
+ NoOp = 6,
+ Invert = 7,
+ Nand = 8,
+ Or = 9,
+ Nor = 10,
+ Xor = 11,
+ Equiv = 12,
+ AndInverted = 13,
+ OrReverse = 14,
+ OrInverted = 15,
+ };
+
+ enum class BlendEquation : u32 {
+ Add = 0,
+ Subtract = 1,
+ ReverseSubtract = 2,
+ Min = 3,
+ Max = 4,
+ };
+
+ enum class BlendFactor : u32 {
+ Zero = 0,
+ One = 1,
+ SourceColor = 2,
+ OneMinusSourceColor = 3,
+ DestColor = 4,
+ OneMinusDestColor = 5,
+ SourceAlpha = 6,
+ OneMinusSourceAlpha = 7,
+ DestAlpha = 8,
+ OneMinusDestAlpha = 9,
+ ConstantColor = 10,
+ OneMinusConstantColor = 11,
+ ConstantAlpha = 12,
+ OneMinusConstantAlpha = 13,
+ SourceAlphaSaturate = 14,
+ };
+
+ enum class CompareFunc : u32 {
+ Never = 0,
+ Always = 1,
+ Equal = 2,
+ NotEqual = 3,
+ LessThan = 4,
+ LessThanOrEqual = 5,
+ GreaterThan = 6,
+ GreaterThanOrEqual = 7,
+ };
+
+ enum class StencilAction : u32 {
+ Keep = 0,
+ Zero = 1,
+ Replace = 2,
+ Increment = 3,
+ Decrement = 4,
+ Invert = 5,
+ IncrementWrap = 6,
+ DecrementWrap = 7,
+ };
+
+ struct {
+ union {
+ // If false, logic blending is used
+ BitField<8, 1, u32> alphablend_enable;
+ };
+
+ union {
+ BitField<0, 3, BlendEquation> blend_equation_rgb;
+ BitField<8, 3, BlendEquation> blend_equation_a;
+
+ BitField<16, 4, BlendFactor> factor_source_rgb;
+ BitField<20, 4, BlendFactor> factor_dest_rgb;
+
+ BitField<24, 4, BlendFactor> factor_source_a;
+ BitField<28, 4, BlendFactor> factor_dest_a;
+ } alpha_blending;
+
+ union {
+ BitField<0, 4, LogicOp> logic_op;
+ };
+
+ union {
+ u32 raw;
+ BitField<0, 8, u32> r;
+ BitField<8, 8, u32> g;
+ BitField<16, 8, u32> b;
+ BitField<24, 8, u32> a;
+ } blend_const;
+
+ union {
+ BitField<0, 1, u32> enable;
+ BitField<4, 3, CompareFunc> func;
+ BitField<8, 8, u32> ref;
+ } alpha_test;
+
+ struct {
+ union {
+ // Raw value of this register
+ u32 raw_func;
+
+ // If true, enable stencil testing
+ BitField<0, 1, u32> enable;
+
+ // Comparison operation for stencil testing
+ BitField<4, 3, CompareFunc> func;
+
+ // Mask used to control writing to the stencil buffer
+ BitField<8, 8, u32> write_mask;
+
+ // Value to compare against for stencil testing
+ BitField<16, 8, u32> reference_value;
+
+ // Mask to apply on stencil test inputs
+ BitField<24, 8, u32> input_mask;
+ };
+
+ union {
+ // Raw value of this register
+ u32 raw_op;
+
+ // Action to perform when the stencil test fails
+ BitField<0, 3, StencilAction> action_stencil_fail;
+
+ // Action to perform when stencil testing passed but depth testing fails
+ BitField<4, 3, StencilAction> action_depth_fail;
+
+ // Action to perform when both stencil and depth testing pass
+ BitField<8, 3, StencilAction> action_depth_pass;
+ };
+ } stencil_test;
+
+ union {
+ BitField<0, 1, u32> depth_test_enable;
+ BitField<4, 3, CompareFunc> depth_test_func;
+ BitField<8, 1, u32> red_enable;
+ BitField<9, 1, u32> green_enable;
+ BitField<10, 1, u32> blue_enable;
+ BitField<11, 1, u32> alpha_enable;
+ BitField<12, 1, u32> depth_write_enable;
+ };
+
+ INSERT_PADDING_WORDS(0x8);
+ } output_merger;
+
+ // Components are laid out in reverse byte order, most significant bits first.
+ enum class ColorFormat : u32 {
+ RGBA8 = 0,
+ RGB8 = 1,
+ RGB5A1 = 2,
+ RGB565 = 3,
+ RGBA4 = 4,
+ };
+
+ enum class DepthFormat : u32 {
+ D16 = 0,
+ D24 = 2,
+ D24S8 = 3,
+ };
+
+ // Returns the number of bytes in the specified color format
+ static unsigned BytesPerColorPixel(ColorFormat format) {
+ switch (format) {
+ case ColorFormat::RGBA8:
+ return 4;
+ case ColorFormat::RGB8:
+ return 3;
+ case ColorFormat::RGB5A1:
+ case ColorFormat::RGB565:
+ case ColorFormat::RGBA4:
+ return 2;
+ default:
+ LOG_CRITICAL(HW_GPU, "Unknown color format %u", format);
+ UNIMPLEMENTED();
+ }
+ }
+
+ struct FramebufferConfig {
+ INSERT_PADDING_WORDS(0x3);
+
+ union {
+ BitField<0, 4, u32> allow_color_write; // 0 = disable, else enable
+ };
+
+ INSERT_PADDING_WORDS(0x1);
+
+ union {
+ BitField<0, 2, u32> allow_depth_stencil_write; // 0 = disable, else enable
+ };
+
+ DepthFormat depth_format; // TODO: Should be a BitField!
+ BitField<16, 3, ColorFormat> color_format;
+
+ INSERT_PADDING_WORDS(0x4);
+
+ u32 depth_buffer_address;
+ u32 color_buffer_address;
+
+ union {
+ // Apparently, the framebuffer width is stored as expected,
+ // while the height is stored as the actual height minus one.
+ // Hence, don't access these fields directly but use the accessors
+ // GetWidth() and GetHeight() instead.
+ BitField<0, 11, u32> width;
+ BitField<12, 10, u32> height;
+ };
+
+ INSERT_PADDING_WORDS(0x1);
+
+ inline PAddr GetColorBufferPhysicalAddress() const {
+ return color_buffer_address * 8;
+ }
+ inline PAddr GetDepthBufferPhysicalAddress() const {
+ return depth_buffer_address * 8;
+ }
+
+ inline u32 GetWidth() const {
+ return width;
+ }
+
+ inline u32 GetHeight() const {
+ return height + 1;
+ }
+ } framebuffer;
+
+ // Returns the number of bytes in the specified depth format
+ static u32 BytesPerDepthPixel(DepthFormat format) {
+ switch (format) {
+ case DepthFormat::D16:
+ return 2;
+ case DepthFormat::D24:
+ return 3;
+ case DepthFormat::D24S8:
+ return 4;
+ default:
+ LOG_CRITICAL(HW_GPU, "Unknown depth format %u", format);
+ UNIMPLEMENTED();
+ }
+ }
+
+ // Returns the number of bits per depth component of the specified depth format
+ static u32 DepthBitsPerPixel(DepthFormat format) {
+ switch (format) {
+ case DepthFormat::D16:
+ return 16;
+ case DepthFormat::D24:
+ case DepthFormat::D24S8:
+ return 24;
+ default:
+ LOG_CRITICAL(HW_GPU, "Unknown depth format %u", format);
+ UNIMPLEMENTED();
+ }
+ }
+
+ INSERT_PADDING_WORDS(0x20);
+};
+
+static_assert(sizeof(FramebufferRegs) == 0x40 * sizeof(u32),
+ "FramebufferRegs struct has incorrect size");
+
+} // namespace Pica
diff --git a/src/video_core/regs_lighting.h b/src/video_core/regs_lighting.h
new file mode 100644
index 000000000..6793405d9
--- /dev/null
+++ b/src/video_core/regs_lighting.h
@@ -0,0 +1,294 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+
+#include "common/assert.h"
+#include "common/bit_field.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/vector_math.h"
+
+namespace Pica {
+
+struct LightingRegs {
+ enum class LightingSampler {
+ Distribution0 = 0,
+ Distribution1 = 1,
+ Fresnel = 3,
+ ReflectBlue = 4,
+ ReflectGreen = 5,
+ ReflectRed = 6,
+ SpotlightAttenuation = 8,
+ DistanceAttenuation = 16,
+ };
+
+ /**
+ * Pica fragment lighting supports using different LUTs for each lighting component: Reflectance
+ * R, G, and B channels, distribution function for specular components 0 and 1, fresnel factor,
+ * and spotlight attenuation. Furthermore, which LUTs are used for each channel (or whether a
+ * channel is enabled at all) is specified by various pre-defined lighting configurations. With
+ * configurations that require more LUTs, more cycles are required on HW to perform lighting
+ * computations.
+ */
+ enum class LightingConfig : u32 {
+ Config0 = 0, ///< Reflect Red, Distribution 0, Spotlight
+ Config1 = 1, ///< Reflect Red, Fresnel, Spotlight
+ Config2 = 2, ///< Reflect Red, Distribution 0/1
+ Config3 = 3, ///< Distribution 0/1, Fresnel
+ Config4 = 4, ///< Reflect Red/Green/Blue, Distribution 0/1, Spotlight
+ Config5 = 5, ///< Reflect Red/Green/Blue, Distribution 0, Fresnel, Spotlight
+ Config6 = 6, ///< Reflect Red, Distribution 0/1, Fresnel, Spotlight
+
+ Config7 = 8, ///< Reflect Red/Green/Blue, Distribution 0/1, Fresnel, Spotlight
+ ///< NOTE: '8' is intentional, '7' does not appear to be a valid configuration
+ };
+
+ /// Selects which lighting components are affected by fresnel
+ enum class LightingFresnelSelector : u32 {
+ None = 0, ///< Fresnel is disabled
+ PrimaryAlpha = 1, ///< Primary (diffuse) lighting alpha is affected by fresnel
+ SecondaryAlpha = 2, ///< Secondary (specular) lighting alpha is affected by fresnel
+ Both =
+ PrimaryAlpha |
+ SecondaryAlpha, ///< Both primary and secondary lighting alphas are affected by fresnel
+ };
+
+ /// Factor used to scale the output of a lighting LUT
+ enum class LightingScale : u32 {
+ Scale1 = 0, ///< Scale is 1x
+ Scale2 = 1, ///< Scale is 2x
+ Scale4 = 2, ///< Scale is 4x
+ Scale8 = 3, ///< Scale is 8x
+
+ Scale1_4 = 6, ///< Scale is 0.25x
+ Scale1_2 = 7, ///< Scale is 0.5x
+ };
+
+ enum class LightingLutInput : u32 {
+ NH = 0, // Cosine of the angle between the normal and half-angle vectors
+ VH = 1, // Cosine of the angle between the view and half-angle vectors
+ NV = 2, // Cosine of the angle between the normal and the view vector
+ LN = 3, // Cosine of the angle between the light and the normal vectors
+ };
+
+ enum class LightingBumpMode : u32 {
+ None = 0,
+ NormalMap = 1,
+ TangentMap = 2,
+ };
+
+ union LightColor {
+ BitField<0, 10, u32> b;
+ BitField<10, 10, u32> g;
+ BitField<20, 10, u32> r;
+
+ Math::Vec3f ToVec3f() const {
+ // These fields are 10 bits wide, however 255 corresponds to 1.0f for each color
+ // component
+ return Math::MakeVec((f32)r / 255.f, (f32)g / 255.f, (f32)b / 255.f);
+ }
+ };
+
+ /// Returns true if the specified lighting sampler is supported by the current Pica lighting
+ /// configuration
+ static bool IsLightingSamplerSupported(LightingConfig config, LightingSampler sampler) {
+ switch (sampler) {
+ case LightingSampler::Distribution0:
+ return (config != LightingConfig::Config1);
+
+ case LightingSampler::Distribution1:
+ return (config != LightingConfig::Config0) && (config != LightingConfig::Config1) &&
+ (config != LightingConfig::Config5);
+
+ case LightingSampler::Fresnel:
+ return (config != LightingConfig::Config0) && (config != LightingConfig::Config2) &&
+ (config != LightingConfig::Config4);
+
+ case LightingSampler::ReflectRed:
+ return (config != LightingConfig::Config3);
+
+ case LightingSampler::ReflectGreen:
+ case LightingSampler::ReflectBlue:
+ return (config == LightingConfig::Config4) || (config == LightingConfig::Config5) ||
+ (config == LightingConfig::Config7);
+ default:
+ UNREACHABLE_MSG("Regs::IsLightingSamplerSupported: Reached "
+ "unreachable section, sampler should be one "
+ "of Distribution0, Distribution1, Fresnel, "
+ "ReflectRed, ReflectGreen or ReflectBlue, instead "
+ "got %i",
+ static_cast<int>(config));
+ }
+ }
+
+ struct LightSrc {
+ LightColor specular_0; // material.specular_0 * light.specular_0
+ LightColor specular_1; // material.specular_1 * light.specular_1
+ LightColor diffuse; // material.diffuse * light.diffuse
+ LightColor ambient; // material.ambient * light.ambient
+
+ // Encoded as 16-bit floating point
+ union {
+ BitField<0, 16, u32> x;
+ BitField<16, 16, u32> y;
+ };
+ union {
+ BitField<0, 16, u32> z;
+ };
+
+ INSERT_PADDING_WORDS(0x3);
+
+ union {
+ BitField<0, 1, u32> directional;
+ BitField<1, 1, u32> two_sided_diffuse; // When disabled, clamp dot-product to 0
+ } config;
+
+ BitField<0, 20, u32> dist_atten_bias;
+ BitField<0, 20, u32> dist_atten_scale;
+
+ INSERT_PADDING_WORDS(0x4);
+ };
+ static_assert(sizeof(LightSrc) == 0x10 * sizeof(u32), "LightSrc structure must be 0x10 words");
+
+ LightSrc light[8];
+ LightColor global_ambient; // Emission + (material.ambient * lighting.ambient)
+ INSERT_PADDING_WORDS(0x1);
+ BitField<0, 3, u32> max_light_index; // Number of enabled lights - 1
+
+ union {
+ BitField<2, 2, LightingFresnelSelector> fresnel_selector;
+ BitField<4, 4, LightingConfig> config;
+ BitField<22, 2, u32> bump_selector; // 0: Texture 0, 1: Texture 1, 2: Texture 2
+ BitField<27, 1, u32> clamp_highlights;
+ BitField<28, 2, LightingBumpMode> bump_mode;
+ BitField<30, 1, u32> disable_bump_renorm;
+ } config0;
+
+ union {
+ BitField<16, 1, u32> disable_lut_d0;
+ BitField<17, 1, u32> disable_lut_d1;
+ BitField<19, 1, u32> disable_lut_fr;
+ BitField<20, 1, u32> disable_lut_rr;
+ BitField<21, 1, u32> disable_lut_rg;
+ BitField<22, 1, u32> disable_lut_rb;
+
+ // Each bit specifies whether distance attenuation should be applied for the corresponding
+ // light.
+ BitField<24, 1, u32> disable_dist_atten_light_0;
+ BitField<25, 1, u32> disable_dist_atten_light_1;
+ BitField<26, 1, u32> disable_dist_atten_light_2;
+ BitField<27, 1, u32> disable_dist_atten_light_3;
+ BitField<28, 1, u32> disable_dist_atten_light_4;
+ BitField<29, 1, u32> disable_dist_atten_light_5;
+ BitField<30, 1, u32> disable_dist_atten_light_6;
+ BitField<31, 1, u32> disable_dist_atten_light_7;
+ } config1;
+
+ bool IsDistAttenDisabled(unsigned index) const {
+ const unsigned disable[] = {
+ config1.disable_dist_atten_light_0, config1.disable_dist_atten_light_1,
+ config1.disable_dist_atten_light_2, config1.disable_dist_atten_light_3,
+ config1.disable_dist_atten_light_4, config1.disable_dist_atten_light_5,
+ config1.disable_dist_atten_light_6, config1.disable_dist_atten_light_7};
+ return disable[index] != 0;
+ }
+
+ union {
+ BitField<0, 8, u32> index; ///< Index at which to set data in the LUT
+ BitField<8, 5, u32> type; ///< Type of LUT for which to set data
+ } lut_config;
+
+ BitField<0, 1, u32> disable;
+ INSERT_PADDING_WORDS(0x1);
+
+ // When data is written to any of these registers, it gets written to the lookup table of the
+ // selected type at the selected index, specified above in the `lut_config` register. With each
+ // write, `lut_config.index` is incremented. It does not matter which of these registers is
+ // written to, the behavior will be the same.
+ u32 lut_data[8];
+
+ // These are used to specify if absolute (abs) value should be used for each LUT index. When
+ // abs mode is disabled, LUT indexes are in the range of (-1.0, 1.0). Otherwise, they are in
+ // the range of (0.0, 1.0).
+ union {
+ BitField<1, 1, u32> disable_d0;
+ BitField<5, 1, u32> disable_d1;
+ BitField<9, 1, u32> disable_sp;
+ BitField<13, 1, u32> disable_fr;
+ BitField<17, 1, u32> disable_rb;
+ BitField<21, 1, u32> disable_rg;
+ BitField<25, 1, u32> disable_rr;
+ } abs_lut_input;
+
+ union {
+ BitField<0, 3, LightingLutInput> d0;
+ BitField<4, 3, LightingLutInput> d1;
+ BitField<8, 3, LightingLutInput> sp;
+ BitField<12, 3, LightingLutInput> fr;
+ BitField<16, 3, LightingLutInput> rb;
+ BitField<20, 3, LightingLutInput> rg;
+ BitField<24, 3, LightingLutInput> rr;
+ } lut_input;
+
+ union {
+ BitField<0, 3, LightingScale> d0;
+ BitField<4, 3, LightingScale> d1;
+ BitField<8, 3, LightingScale> sp;
+ BitField<12, 3, LightingScale> fr;
+ BitField<16, 3, LightingScale> rb;
+ BitField<20, 3, LightingScale> rg;
+ BitField<24, 3, LightingScale> rr;
+
+ static float GetScale(LightingScale scale) {
+ switch (scale) {
+ case LightingScale::Scale1:
+ return 1.0f;
+ case LightingScale::Scale2:
+ return 2.0f;
+ case LightingScale::Scale4:
+ return 4.0f;
+ case LightingScale::Scale8:
+ return 8.0f;
+ case LightingScale::Scale1_4:
+ return 0.25f;
+ case LightingScale::Scale1_2:
+ return 0.5f;
+ }
+ return 0.0f;
+ }
+ } lut_scale;
+
+ INSERT_PADDING_WORDS(0x6);
+
+ union {
+ // There are 8 light enable "slots", corresponding to the total number of lights supported
+ // by Pica. For N enabled lights (specified by register 0x1c2, or 'src_num' above), the
+ // first N slots below will be set to integers within the range of 0-7, corresponding to the
+ // actual light that is enabled for each slot.
+
+ BitField<0, 3, u32> slot_0;
+ BitField<4, 3, u32> slot_1;
+ BitField<8, 3, u32> slot_2;
+ BitField<12, 3, u32> slot_3;
+ BitField<16, 3, u32> slot_4;
+ BitField<20, 3, u32> slot_5;
+ BitField<24, 3, u32> slot_6;
+ BitField<28, 3, u32> slot_7;
+
+ unsigned GetNum(unsigned index) const {
+ const unsigned enable_slots[] = {slot_0, slot_1, slot_2, slot_3,
+ slot_4, slot_5, slot_6, slot_7};
+ return enable_slots[index];
+ }
+ } light_enable;
+
+ INSERT_PADDING_WORDS(0x26);
+};
+
+static_assert(sizeof(LightingRegs) == 0xC0 * sizeof(u32), "LightingRegs struct has incorrect size");
+
+} // namespace Pica
diff --git a/src/video_core/regs_pipeline.h b/src/video_core/regs_pipeline.h
new file mode 100644
index 000000000..0a4ec6e1e
--- /dev/null
+++ b/src/video_core/regs_pipeline.h
@@ -0,0 +1,230 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+
+#include "common/assert.h"
+#include "common/bit_field.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+
+namespace Pica {
+
+struct PipelineRegs {
+ enum class VertexAttributeFormat : u32 {
+ BYTE = 0,
+ UBYTE = 1,
+ SHORT = 2,
+ FLOAT = 3,
+ };
+
+ struct {
+ BitField<0, 29, u32> base_address;
+
+ PAddr GetPhysicalBaseAddress() const {
+ return base_address * 8;
+ }
+
+ // Descriptor for internal vertex attributes
+ union {
+ BitField<0, 2, VertexAttributeFormat> format0; // size of one element
+ BitField<2, 2, u32> size0; // number of elements minus 1
+ BitField<4, 2, VertexAttributeFormat> format1;
+ BitField<6, 2, u32> size1;
+ BitField<8, 2, VertexAttributeFormat> format2;
+ BitField<10, 2, u32> size2;
+ BitField<12, 2, VertexAttributeFormat> format3;
+ BitField<14, 2, u32> size3;
+ BitField<16, 2, VertexAttributeFormat> format4;
+ BitField<18, 2, u32> size4;
+ BitField<20, 2, VertexAttributeFormat> format5;
+ BitField<22, 2, u32> size5;
+ BitField<24, 2, VertexAttributeFormat> format6;
+ BitField<26, 2, u32> size6;
+ BitField<28, 2, VertexAttributeFormat> format7;
+ BitField<30, 2, u32> size7;
+ };
+
+ union {
+ BitField<0, 2, VertexAttributeFormat> format8;
+ BitField<2, 2, u32> size8;
+ BitField<4, 2, VertexAttributeFormat> format9;
+ BitField<6, 2, u32> size9;
+ BitField<8, 2, VertexAttributeFormat> format10;
+ BitField<10, 2, u32> size10;
+ BitField<12, 2, VertexAttributeFormat> format11;
+ BitField<14, 2, u32> size11;
+
+ BitField<16, 12, u32> attribute_mask;
+
+ // number of total attributes minus 1
+ BitField<28, 4, u32> max_attribute_index;
+ };
+
+ inline VertexAttributeFormat GetFormat(int n) const {
+ VertexAttributeFormat formats[] = {format0, format1, format2, format3,
+ format4, format5, format6, format7,
+ format8, format9, format10, format11};
+ return formats[n];
+ }
+
+ inline int GetNumElements(int n) const {
+ u32 sizes[] = {size0, size1, size2, size3, size4, size5,
+ size6, size7, size8, size9, size10, size11};
+ return (int)sizes[n] + 1;
+ }
+
+ inline int GetElementSizeInBytes(int n) const {
+ return (GetFormat(n) == VertexAttributeFormat::FLOAT)
+ ? 4
+ : (GetFormat(n) == VertexAttributeFormat::SHORT) ? 2 : 1;
+ }
+
+ inline int GetStride(int n) const {
+ return GetNumElements(n) * GetElementSizeInBytes(n);
+ }
+
+ inline bool IsDefaultAttribute(int id) const {
+ return (id >= 12) || (attribute_mask & (1ULL << id)) != 0;
+ }
+
+ inline int GetNumTotalAttributes() const {
+ return (int)max_attribute_index + 1;
+ }
+
+ // Attribute loaders map the source vertex data to input attributes
+ // This e.g. allows to load different attributes from different memory locations
+ struct {
+ // Source attribute data offset from the base address
+ u32 data_offset;
+
+ union {
+ BitField<0, 4, u32> comp0;
+ BitField<4, 4, u32> comp1;
+ BitField<8, 4, u32> comp2;
+ BitField<12, 4, u32> comp3;
+ BitField<16, 4, u32> comp4;
+ BitField<20, 4, u32> comp5;
+ BitField<24, 4, u32> comp6;
+ BitField<28, 4, u32> comp7;
+ };
+
+ union {
+ BitField<0, 4, u32> comp8;
+ BitField<4, 4, u32> comp9;
+ BitField<8, 4, u32> comp10;
+ BitField<12, 4, u32> comp11;
+
+ // bytes for a single vertex in this loader
+ BitField<16, 8, u32> byte_count;
+
+ BitField<28, 4, u32> component_count;
+ };
+
+ inline int GetComponent(int n) const {
+ u32 components[] = {comp0, comp1, comp2, comp3, comp4, comp5,
+ comp6, comp7, comp8, comp9, comp10, comp11};
+ return (int)components[n];
+ }
+ } attribute_loaders[12];
+ } vertex_attributes;
+
+ struct {
+ enum IndexFormat : u32 {
+ BYTE = 0,
+ SHORT = 1,
+ };
+
+ union {
+ BitField<0, 31, u32> offset; // relative to base attribute address
+ BitField<31, 1, IndexFormat> format;
+ };
+ } index_array;
+
+ // Number of vertices to render
+ u32 num_vertices;
+
+ INSERT_PADDING_WORDS(0x1);
+
+ // The index of the first vertex to render
+ u32 vertex_offset;
+
+ INSERT_PADDING_WORDS(0x3);
+
+ // These two trigger rendering of triangles
+ u32 trigger_draw;
+ u32 trigger_draw_indexed;
+
+ INSERT_PADDING_WORDS(0x2);
+
+ // These registers are used to setup the default "fall-back" vertex shader attributes
+ struct {
+ // Index of the current default attribute
+ u32 index;
+
+ // Writing to these registers sets the "current" default attribute.
+ u32 set_value[3];
+ } vs_default_attributes_setup;
+
+ INSERT_PADDING_WORDS(0x2);
+
+ struct {
+ // There are two channels that can be used to configure the next command buffer, which can
+ // be then executed by writing to the "trigger" registers. There are two reasons why a game
+ // might use this feature:
+ // 1) With this, an arbitrary number of additional command buffers may be executed in
+ // sequence without requiring any intervention of the CPU after the initial one is
+ // kicked off.
+ // 2) Games can configure these registers to provide a command list subroutine mechanism.
+
+ BitField<0, 20, u32> size[2]; ///< Size (in bytes / 8) of each channel's command buffer
+ BitField<0, 28, u32> addr[2]; ///< Physical address / 8 of each channel's command buffer
+ u32 trigger[2]; ///< Triggers execution of the channel's command buffer when written to
+
+ unsigned GetSize(unsigned index) const {
+ ASSERT(index < 2);
+ return 8 * size[index];
+ }
+
+ PAddr GetPhysicalAddress(unsigned index) const {
+ ASSERT(index < 2);
+ return (PAddr)(8 * addr[index]);
+ }
+ } command_buffer;
+
+ INSERT_PADDING_WORDS(4);
+
+ /// Number of input attributes to the vertex shader minus 1
+ BitField<0, 4, u32> max_input_attrib_index;
+
+ INSERT_PADDING_WORDS(2);
+
+ enum class GPUMode : u32 {
+ Drawing = 0,
+ Configuring = 1,
+ };
+
+ GPUMode gpu_mode;
+
+ INSERT_PADDING_WORDS(0x18);
+
+ enum class TriangleTopology : u32 {
+ List = 0,
+ Strip = 1,
+ Fan = 2,
+ Shader = 3, // Programmable setup unit implemented in a geometry shader
+ };
+
+ BitField<8, 2, TriangleTopology> triangle_topology;
+
+ u32 restart_primitive;
+
+ INSERT_PADDING_WORDS(0x20);
+};
+
+static_assert(sizeof(PipelineRegs) == 0x80 * sizeof(u32), "PipelineRegs struct has incorrect size");
+
+} // namespace Pica
diff --git a/src/video_core/regs_rasterizer.h b/src/video_core/regs_rasterizer.h
new file mode 100644
index 000000000..a471a3b38
--- /dev/null
+++ b/src/video_core/regs_rasterizer.h
@@ -0,0 +1,129 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+
+#include "common/bit_field.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+
+namespace Pica {
+
+struct RasterizerRegs {
+ enum class CullMode : u32 {
+ // Select which polygons are considered to be "frontfacing".
+ KeepAll = 0,
+ KeepClockWise = 1,
+ KeepCounterClockWise = 2,
+ // TODO: What does the third value imply?
+ };
+
+ union {
+ BitField<0, 2, CullMode> cull_mode;
+ };
+
+ BitField<0, 24, u32> viewport_size_x;
+
+ INSERT_PADDING_WORDS(0x1);
+
+ BitField<0, 24, u32> viewport_size_y;
+
+ INSERT_PADDING_WORDS(0x9);
+
+ BitField<0, 24, u32> viewport_depth_range; // float24
+ BitField<0, 24, u32> viewport_depth_near_plane; // float24
+
+ BitField<0, 3, u32> vs_output_total;
+
+ union VSOutputAttributes {
+ // Maps components of output vertex attributes to semantics
+ enum Semantic : u32 {
+ POSITION_X = 0,
+ POSITION_Y = 1,
+ POSITION_Z = 2,
+ POSITION_W = 3,
+
+ QUATERNION_X = 4,
+ QUATERNION_Y = 5,
+ QUATERNION_Z = 6,
+ QUATERNION_W = 7,
+
+ COLOR_R = 8,
+ COLOR_G = 9,
+ COLOR_B = 10,
+ COLOR_A = 11,
+
+ TEXCOORD0_U = 12,
+ TEXCOORD0_V = 13,
+ TEXCOORD1_U = 14,
+ TEXCOORD1_V = 15,
+
+ TEXCOORD0_W = 16,
+
+ VIEW_X = 18,
+ VIEW_Y = 19,
+ VIEW_Z = 20,
+
+ TEXCOORD2_U = 22,
+ TEXCOORD2_V = 23,
+
+ INVALID = 31,
+ };
+
+ BitField<0, 5, Semantic> map_x;
+ BitField<8, 5, Semantic> map_y;
+ BitField<16, 5, Semantic> map_z;
+ BitField<24, 5, Semantic> map_w;
+ } vs_output_attributes[7];
+
+ INSERT_PADDING_WORDS(0xe);
+
+ enum class ScissorMode : u32 {
+ Disabled = 0,
+ Exclude = 1, // Exclude pixels inside the scissor box
+
+ Include = 3 // Exclude pixels outside the scissor box
+ };
+
+ struct {
+ BitField<0, 2, ScissorMode> mode;
+
+ union {
+ BitField<0, 16, u32> x1;
+ BitField<16, 16, u32> y1;
+ };
+
+ union {
+ BitField<0, 16, u32> x2;
+ BitField<16, 16, u32> y2;
+ };
+ } scissor_test;
+
+ union {
+ BitField<0, 10, s32> x;
+ BitField<16, 10, s32> y;
+ } viewport_corner;
+
+ INSERT_PADDING_WORDS(0x1);
+
+ // TODO: early depth
+ INSERT_PADDING_WORDS(0x1);
+
+ INSERT_PADDING_WORDS(0x2);
+
+ enum DepthBuffering : u32 {
+ WBuffering = 0,
+ ZBuffering = 1,
+ };
+ BitField<0, 1, DepthBuffering> depthmap_enable;
+
+ INSERT_PADDING_WORDS(0x12);
+};
+
+static_assert(sizeof(RasterizerRegs) == 0x40 * sizeof(u32),
+ "RasterizerRegs struct has incorrect size");
+
+} // namespace Pica
diff --git a/src/video_core/regs_shader.h b/src/video_core/regs_shader.h
new file mode 100644
index 000000000..ddb1ee451
--- /dev/null
+++ b/src/video_core/regs_shader.h
@@ -0,0 +1,104 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+
+#include "common/bit_field.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+
+namespace Pica {
+
+struct ShaderRegs {
+ BitField<0, 16, u32> bool_uniforms;
+
+ union {
+ BitField<0, 8, u32> x;
+ BitField<8, 8, u32> y;
+ BitField<16, 8, u32> z;
+ BitField<24, 8, u32> w;
+ } int_uniforms[4];
+
+ INSERT_PADDING_WORDS(0x4);
+
+ union {
+ // Number of input attributes to shader unit - 1
+ BitField<0, 4, u32> max_input_attribute_index;
+ };
+
+ // Offset to shader program entry point (in words)
+ BitField<0, 16, u32> main_offset;
+
+ /// Maps input attributes to registers. 4-bits per attribute, specifying a register index
+ u32 input_attribute_to_register_map_low;
+ u32 input_attribute_to_register_map_high;
+
+ unsigned int GetRegisterForAttribute(unsigned int attribute_index) const {
+ u64 map = ((u64)input_attribute_to_register_map_high << 32) |
+ (u64)input_attribute_to_register_map_low;
+ return (map >> (attribute_index * 4)) & 0b1111;
+ }
+
+ BitField<0, 16, u32> output_mask;
+
+ // 0x28E, CODETRANSFER_END
+ INSERT_PADDING_WORDS(0x2);
+
+ struct {
+ enum Format : u32 {
+ FLOAT24 = 0,
+ FLOAT32 = 1,
+ };
+
+ bool IsFloat32() const {
+ return format == FLOAT32;
+ }
+
+ union {
+ // Index of the next uniform to write to
+ // TODO: ctrulib uses 8 bits for this, however that seems to yield lots of invalid
+ // indices
+ // TODO: Maybe the uppermost index is for the geometry shader? Investigate!
+ BitField<0, 7, u32> index;
+
+ BitField<31, 1, Format> format;
+ };
+
+ // Writing to these registers sets the current uniform.
+ u32 set_value[8];
+
+ } uniform_setup;
+
+ INSERT_PADDING_WORDS(0x2);
+
+ struct {
+ // Offset of the next instruction to write code to.
+ // Incremented with each instruction write.
+ u32 offset;
+
+ // Writing to these registers sets the "current" word in the shader program.
+ u32 set_word[8];
+ } program;
+
+ INSERT_PADDING_WORDS(0x1);
+
+ // This register group is used to load an internal table of swizzling patterns,
+ // which are indexed by each shader instruction to specify vector component swizzling.
+ struct {
+ // Offset of the next swizzle pattern to write code to.
+ // Incremented with each instruction write.
+ u32 offset;
+
+ // Writing to these registers sets the current swizzle pattern in the table.
+ u32 set_word[8];
+ } swizzle_patterns;
+
+ INSERT_PADDING_WORDS(0x2);
+};
+
+static_assert(sizeof(ShaderRegs) == 0x30 * sizeof(u32), "ShaderRegs struct has incorrect size");
+
+} // namespace Pica
diff --git a/src/video_core/regs_texturing.h b/src/video_core/regs_texturing.h
new file mode 100644
index 000000000..be8bc6826
--- /dev/null
+++ b/src/video_core/regs_texturing.h
@@ -0,0 +1,328 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+
+#include "common/assert.h"
+#include "common/bit_field.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+
+namespace Pica {
+
+struct TexturingRegs {
+ struct TextureConfig {
+ enum TextureType : u32 {
+ Texture2D = 0,
+ TextureCube = 1,
+ Shadow2D = 2,
+ Projection2D = 3,
+ ShadowCube = 4,
+ Disabled = 5,
+ };
+
+ enum WrapMode : u32 {
+ ClampToEdge = 0,
+ ClampToBorder = 1,
+ Repeat = 2,
+ MirroredRepeat = 3,
+ };
+
+ enum TextureFilter : u32 {
+ Nearest = 0,
+ Linear = 1,
+ };
+
+ union {
+ u32 raw;
+ BitField<0, 8, u32> r;
+ BitField<8, 8, u32> g;
+ BitField<16, 8, u32> b;
+ BitField<24, 8, u32> a;
+ } border_color;
+
+ union {
+ BitField<0, 16, u32> height;
+ BitField<16, 16, u32> width;
+ };
+
+ union {
+ BitField<1, 1, TextureFilter> mag_filter;
+ BitField<2, 1, TextureFilter> min_filter;
+ BitField<8, 2, WrapMode> wrap_t;
+ BitField<12, 2, WrapMode> wrap_s;
+ BitField<28, 2, TextureType>
+ type; ///< @note Only valid for texture 0 according to 3DBrew.
+ };
+
+ INSERT_PADDING_WORDS(0x1);
+
+ u32 address;
+
+ PAddr GetPhysicalAddress() const {
+ return address * 8;
+ }
+
+ // texture1 and texture2 store the texture format directly after the address
+ // whereas texture0 inserts some additional flags inbetween.
+ // Hence, we store the format separately so that all other parameters can be described
+ // in a single structure.
+ };
+
+ enum class TextureFormat : u32 {
+ RGBA8 = 0,
+ RGB8 = 1,
+ RGB5A1 = 2,
+ RGB565 = 3,
+ RGBA4 = 4,
+ IA8 = 5,
+ RG8 = 6, ///< @note Also called HILO8 in 3DBrew.
+ I8 = 7,
+ A8 = 8,
+ IA4 = 9,
+ I4 = 10,
+ A4 = 11,
+ ETC1 = 12, // compressed
+ ETC1A4 = 13, // compressed
+ };
+
+ static unsigned NibblesPerPixel(TextureFormat format) {
+ switch (format) {
+ case TextureFormat::RGBA8:
+ return 8;
+
+ case TextureFormat::RGB8:
+ return 6;
+
+ case TextureFormat::RGB5A1:
+ case TextureFormat::RGB565:
+ case TextureFormat::RGBA4:
+ case TextureFormat::IA8:
+ case TextureFormat::RG8:
+ return 4;
+
+ case TextureFormat::I4:
+ case TextureFormat::A4:
+ return 1;
+
+ case TextureFormat::I8:
+ case TextureFormat::A8:
+ case TextureFormat::IA4:
+
+ default: // placeholder for yet unknown formats
+ UNIMPLEMENTED();
+ return 0;
+ }
+ }
+
+ union {
+ BitField<0, 1, u32> texture0_enable;
+ BitField<1, 1, u32> texture1_enable;
+ BitField<2, 1, u32> texture2_enable;
+ };
+ TextureConfig texture0;
+ INSERT_PADDING_WORDS(0x8);
+ BitField<0, 4, TextureFormat> texture0_format;
+ BitField<0, 1, u32> fragment_lighting_enable;
+ INSERT_PADDING_WORDS(0x1);
+ TextureConfig texture1;
+ BitField<0, 4, TextureFormat> texture1_format;
+ INSERT_PADDING_WORDS(0x2);
+ TextureConfig texture2;
+ BitField<0, 4, TextureFormat> texture2_format;
+ INSERT_PADDING_WORDS(0x21);
+
+ struct FullTextureConfig {
+ const bool enabled;
+ const TextureConfig config;
+ const TextureFormat format;
+ };
+ const std::array<FullTextureConfig, 3> GetTextures() const {
+ return {{
+ {texture0_enable.ToBool(), texture0, texture0_format},
+ {texture1_enable.ToBool(), texture1, texture1_format},
+ {texture2_enable.ToBool(), texture2, texture2_format},
+ }};
+ }
+
+ // 0xc0-0xff: Texture Combiner (akin to glTexEnv)
+ struct TevStageConfig {
+ enum class Source : u32 {
+ PrimaryColor = 0x0,
+ PrimaryFragmentColor = 0x1,
+ SecondaryFragmentColor = 0x2,
+
+ Texture0 = 0x3,
+ Texture1 = 0x4,
+ Texture2 = 0x5,
+ Texture3 = 0x6,
+
+ PreviousBuffer = 0xd,
+ Constant = 0xe,
+ Previous = 0xf,
+ };
+
+ enum class ColorModifier : u32 {
+ SourceColor = 0x0,
+ OneMinusSourceColor = 0x1,
+ SourceAlpha = 0x2,
+ OneMinusSourceAlpha = 0x3,
+ SourceRed = 0x4,
+ OneMinusSourceRed = 0x5,
+
+ SourceGreen = 0x8,
+ OneMinusSourceGreen = 0x9,
+
+ SourceBlue = 0xc,
+ OneMinusSourceBlue = 0xd,
+ };
+
+ enum class AlphaModifier : u32 {
+ SourceAlpha = 0x0,
+ OneMinusSourceAlpha = 0x1,
+ SourceRed = 0x2,
+ OneMinusSourceRed = 0x3,
+ SourceGreen = 0x4,
+ OneMinusSourceGreen = 0x5,
+ SourceBlue = 0x6,
+ OneMinusSourceBlue = 0x7,
+ };
+
+ enum class Operation : u32 {
+ Replace = 0,
+ Modulate = 1,
+ Add = 2,
+ AddSigned = 3,
+ Lerp = 4,
+ Subtract = 5,
+ Dot3_RGB = 6,
+
+ MultiplyThenAdd = 8,
+ AddThenMultiply = 9,
+ };
+
+ union {
+ u32 sources_raw;
+ BitField<0, 4, Source> color_source1;
+ BitField<4, 4, Source> color_source2;
+ BitField<8, 4, Source> color_source3;
+ BitField<16, 4, Source> alpha_source1;
+ BitField<20, 4, Source> alpha_source2;
+ BitField<24, 4, Source> alpha_source3;
+ };
+
+ union {
+ u32 modifiers_raw;
+ BitField<0, 4, ColorModifier> color_modifier1;
+ BitField<4, 4, ColorModifier> color_modifier2;
+ BitField<8, 4, ColorModifier> color_modifier3;
+ BitField<12, 3, AlphaModifier> alpha_modifier1;
+ BitField<16, 3, AlphaModifier> alpha_modifier2;
+ BitField<20, 3, AlphaModifier> alpha_modifier3;
+ };
+
+ union {
+ u32 ops_raw;
+ BitField<0, 4, Operation> color_op;
+ BitField<16, 4, Operation> alpha_op;
+ };
+
+ union {
+ u32 const_color;
+ BitField<0, 8, u32> const_r;
+ BitField<8, 8, u32> const_g;
+ BitField<16, 8, u32> const_b;
+ BitField<24, 8, u32> const_a;
+ };
+
+ union {
+ u32 scales_raw;
+ BitField<0, 2, u32> color_scale;
+ BitField<16, 2, u32> alpha_scale;
+ };
+
+ inline unsigned GetColorMultiplier() const {
+ return (color_scale < 3) ? (1 << color_scale) : 1;
+ }
+
+ inline unsigned GetAlphaMultiplier() const {
+ return (alpha_scale < 3) ? (1 << alpha_scale) : 1;
+ }
+ };
+
+ TevStageConfig tev_stage0;
+ INSERT_PADDING_WORDS(0x3);
+ TevStageConfig tev_stage1;
+ INSERT_PADDING_WORDS(0x3);
+ TevStageConfig tev_stage2;
+ INSERT_PADDING_WORDS(0x3);
+ TevStageConfig tev_stage3;
+ INSERT_PADDING_WORDS(0x3);
+
+ enum class FogMode : u32 {
+ None = 0,
+ Fog = 5,
+ Gas = 7,
+ };
+
+ union {
+ BitField<0, 3, FogMode> fog_mode;
+ BitField<16, 1, u32> fog_flip;
+
+ union {
+ // Tev stages 0-3 write their output to the combiner buffer if the corresponding bit in
+ // these masks are set
+ BitField<8, 4, u32> update_mask_rgb;
+ BitField<12, 4, u32> update_mask_a;
+
+ bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const {
+ return (stage_index < 4) && (update_mask_rgb & (1 << stage_index));
+ }
+
+ bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const {
+ return (stage_index < 4) && (update_mask_a & (1 << stage_index));
+ }
+ } tev_combiner_buffer_input;
+ };
+
+ union {
+ u32 raw;
+ BitField<0, 8, u32> r;
+ BitField<8, 8, u32> g;
+ BitField<16, 8, u32> b;
+ } fog_color;
+
+ INSERT_PADDING_WORDS(0x4);
+
+ BitField<0, 16, u32> fog_lut_offset;
+
+ INSERT_PADDING_WORDS(0x1);
+
+ u32 fog_lut_data[8];
+
+ TevStageConfig tev_stage4;
+ INSERT_PADDING_WORDS(0x3);
+ TevStageConfig tev_stage5;
+
+ union {
+ u32 raw;
+ BitField<0, 8, u32> r;
+ BitField<8, 8, u32> g;
+ BitField<16, 8, u32> b;
+ BitField<24, 8, u32> a;
+ } tev_combiner_buffer_color;
+
+ INSERT_PADDING_WORDS(0x2);
+
+ const std::array<TevStageConfig, 6> GetTevStages() const {
+ return {{tev_stage0, tev_stage1, tev_stage2, tev_stage3, tev_stage4, tev_stage5}};
+ };
+};
+
+static_assert(sizeof(TexturingRegs) == 0x80 * sizeof(u32),
+ "TexturingRegs struct has incorrect size");
+
+} // namespace Pica
diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp
index fd38175b3..f6ece5c4b 100644
--- a/src/video_core/renderer_base.cpp
+++ b/src/video_core/renderer_base.cpp
@@ -6,7 +6,7 @@
#include <memory>
#include "video_core/renderer_base.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
-#include "video_core/swrasterizer.h"
+#include "video_core/swrasterizer/swrasterizer.h"
#include "video_core/video_core.h"
void RendererBase::RefreshRasterizerSetting() {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index f3674e965..de1d5eba7 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -14,8 +14,10 @@
#include "common/microprofile.h"
#include "common/vector_math.h"
#include "core/hw/gpu.h"
-#include "video_core/pica.h"
#include "video_core/pica_state.h"
+#include "video_core/regs_framebuffer.h"
+#include "video_core/regs_rasterizer.h"
+#include "video_core/regs_texturing.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_gen.h"
#include "video_core/renderer_opengl/gl_shader_util.h"
@@ -26,16 +28,6 @@ MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255));
MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100));
-static bool IsPassThroughTevStage(const Pica::Regs::TevStageConfig& stage) {
- return (stage.color_op == Pica::Regs::TevStageConfig::Operation::Replace &&
- stage.alpha_op == Pica::Regs::TevStageConfig::Operation::Replace &&
- stage.color_source1 == Pica::Regs::TevStageConfig::Source::Previous &&
- stage.alpha_source1 == Pica::Regs::TevStageConfig::Source::Previous &&
- stage.color_modifier1 == Pica::Regs::TevStageConfig::ColorModifier::SourceColor &&
- stage.alpha_modifier1 == Pica::Regs::TevStageConfig::AlphaModifier::SourceAlpha &&
- stage.GetColorMultiplier() == 1 && stage.GetAlphaMultiplier() == 1);
-}
-
RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {
// Create sampler objects
for (size_t i = 0; i < texture_samplers.size(); ++i) {
@@ -181,7 +173,7 @@ void RasterizerOpenGL::DrawTriangles() {
CachedSurface* depth_surface;
MathUtil::Rectangle<int> rect;
std::tie(color_surface, depth_surface, rect) =
- res_cache.GetFramebufferSurfaces(regs.framebuffer);
+ res_cache.GetFramebufferSurfaces(regs.framebuffer.framebuffer);
state.draw.draw_framebuffer = framebuffer.handle;
state.Apply();
@@ -190,20 +182,24 @@ void RasterizerOpenGL::DrawTriangles() {
color_surface != nullptr ? color_surface->texture.handle : 0, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
depth_surface != nullptr ? depth_surface->texture.handle : 0, 0);
- bool has_stencil = regs.framebuffer.depth_format == Pica::Regs::DepthFormat::D24S8;
+ bool has_stencil =
+ regs.framebuffer.framebuffer.depth_format == Pica::FramebufferRegs::DepthFormat::D24S8;
glFramebufferTexture2D(
GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
(has_stencil && depth_surface != nullptr) ? depth_surface->texture.handle : 0, 0);
// Sync the viewport
// These registers hold half-width and half-height, so must be multiplied by 2
- GLsizei viewport_width = (GLsizei)Pica::float24::FromRaw(regs.viewport_size_x).ToFloat32() * 2;
- GLsizei viewport_height = (GLsizei)Pica::float24::FromRaw(regs.viewport_size_y).ToFloat32() * 2;
+ GLsizei viewport_width =
+ (GLsizei)Pica::float24::FromRaw(regs.rasterizer.viewport_size_x).ToFloat32() * 2;
+ GLsizei viewport_height =
+ (GLsizei)Pica::float24::FromRaw(regs.rasterizer.viewport_size_y).ToFloat32() * 2;
- glViewport((GLint)(rect.left + regs.viewport_corner.x * color_surface->res_scale_width),
- (GLint)(rect.bottom + regs.viewport_corner.y * color_surface->res_scale_height),
- (GLsizei)(viewport_width * color_surface->res_scale_width),
- (GLsizei)(viewport_height * color_surface->res_scale_height));
+ glViewport(
+ (GLint)(rect.left + regs.rasterizer.viewport_corner.x * color_surface->res_scale_width),
+ (GLint)(rect.bottom + regs.rasterizer.viewport_corner.y * color_surface->res_scale_height),
+ (GLsizei)(viewport_width * color_surface->res_scale_width),
+ (GLsizei)(viewport_height * color_surface->res_scale_height));
if (uniform_block_data.data.framebuffer_scale[0] != color_surface->res_scale_width ||
uniform_block_data.data.framebuffer_scale[1] != color_surface->res_scale_height) {
@@ -215,16 +211,16 @@ void RasterizerOpenGL::DrawTriangles() {
// Scissor checks are window-, not viewport-relative, which means that if the cached texture
// sub-rect changes, the scissor bounds also need to be updated.
- GLint scissor_x1 =
- static_cast<GLint>(rect.left + regs.scissor_test.x1 * color_surface->res_scale_width);
- GLint scissor_y1 =
- static_cast<GLint>(rect.bottom + regs.scissor_test.y1 * color_surface->res_scale_height);
+ GLint scissor_x1 = static_cast<GLint>(
+ rect.left + regs.rasterizer.scissor_test.x1 * color_surface->res_scale_width);
+ GLint scissor_y1 = static_cast<GLint>(
+ rect.bottom + regs.rasterizer.scissor_test.y1 * color_surface->res_scale_height);
// x2, y2 have +1 added to cover the entire pixel area, otherwise you might get cracks when
// scaling or doing multisampling.
- GLint scissor_x2 =
- static_cast<GLint>(rect.left + (regs.scissor_test.x2 + 1) * color_surface->res_scale_width);
+ GLint scissor_x2 = static_cast<GLint>(
+ rect.left + (regs.rasterizer.scissor_test.x2 + 1) * color_surface->res_scale_width);
GLint scissor_y2 = static_cast<GLint>(
- rect.bottom + (regs.scissor_test.y2 + 1) * color_surface->res_scale_height);
+ rect.bottom + (regs.rasterizer.scissor_test.y2 + 1) * color_surface->res_scale_height);
if (uniform_block_data.data.scissor_x1 != scissor_x1 ||
uniform_block_data.data.scissor_x2 != scissor_x2 ||
@@ -239,7 +235,7 @@ void RasterizerOpenGL::DrawTriangles() {
}
// Sync and bind the texture surfaces
- const auto pica_textures = regs.GetTextures();
+ const auto pica_textures = regs.texturing.GetTextures();
for (unsigned texture_index = 0; texture_index < pica_textures.size(); ++texture_index) {
const auto& texture = pica_textures[texture_index];
@@ -316,69 +312,69 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
switch (id) {
// Culling
- case PICA_REG_INDEX(cull_mode):
+ case PICA_REG_INDEX(rasterizer.cull_mode):
SyncCullMode();
break;
// Depth modifiers
- case PICA_REG_INDEX(viewport_depth_range):
+ case PICA_REG_INDEX(rasterizer.viewport_depth_range):
SyncDepthScale();
break;
- case PICA_REG_INDEX(viewport_depth_near_plane):
+ case PICA_REG_INDEX(rasterizer.viewport_depth_near_plane):
SyncDepthOffset();
break;
// Depth buffering
- case PICA_REG_INDEX(depthmap_enable):
+ case PICA_REG_INDEX(rasterizer.depthmap_enable):
shader_dirty = true;
break;
// Blending
- case PICA_REG_INDEX(output_merger.alphablend_enable):
+ case PICA_REG_INDEX(framebuffer.output_merger.alphablend_enable):
SyncBlendEnabled();
break;
- case PICA_REG_INDEX(output_merger.alpha_blending):
+ case PICA_REG_INDEX(framebuffer.output_merger.alpha_blending):
SyncBlendFuncs();
break;
- case PICA_REG_INDEX(output_merger.blend_const):
+ case PICA_REG_INDEX(framebuffer.output_merger.blend_const):
SyncBlendColor();
break;
// Fog state
- case PICA_REG_INDEX(fog_color):
+ case PICA_REG_INDEX(texturing.fog_color):
SyncFogColor();
break;
- case PICA_REG_INDEX_WORKAROUND(fog_lut_data[0], 0xe8):
- case PICA_REG_INDEX_WORKAROUND(fog_lut_data[1], 0xe9):
- case PICA_REG_INDEX_WORKAROUND(fog_lut_data[2], 0xea):
- case PICA_REG_INDEX_WORKAROUND(fog_lut_data[3], 0xeb):
- case PICA_REG_INDEX_WORKAROUND(fog_lut_data[4], 0xec):
- case PICA_REG_INDEX_WORKAROUND(fog_lut_data[5], 0xed):
- case PICA_REG_INDEX_WORKAROUND(fog_lut_data[6], 0xee):
- case PICA_REG_INDEX_WORKAROUND(fog_lut_data[7], 0xef):
+ case PICA_REG_INDEX_WORKAROUND(texturing.fog_lut_data[0], 0xe8):
+ case PICA_REG_INDEX_WORKAROUND(texturing.fog_lut_data[1], 0xe9):
+ case PICA_REG_INDEX_WORKAROUND(texturing.fog_lut_data[2], 0xea):
+ case PICA_REG_INDEX_WORKAROUND(texturing.fog_lut_data[3], 0xeb):
+ case PICA_REG_INDEX_WORKAROUND(texturing.fog_lut_data[4], 0xec):
+ case PICA_REG_INDEX_WORKAROUND(texturing.fog_lut_data[5], 0xed):
+ case PICA_REG_INDEX_WORKAROUND(texturing.fog_lut_data[6], 0xee):
+ case PICA_REG_INDEX_WORKAROUND(texturing.fog_lut_data[7], 0xef):
uniform_block_data.fog_lut_dirty = true;
break;
// Alpha test
- case PICA_REG_INDEX(output_merger.alpha_test):
+ case PICA_REG_INDEX(framebuffer.output_merger.alpha_test):
SyncAlphaTest();
shader_dirty = true;
break;
// Sync GL stencil test + stencil write mask
// (Pica stencil test function register also contains a stencil write mask)
- case PICA_REG_INDEX(output_merger.stencil_test.raw_func):
+ case PICA_REG_INDEX(framebuffer.output_merger.stencil_test.raw_func):
SyncStencilTest();
SyncStencilWriteMask();
break;
- case PICA_REG_INDEX(output_merger.stencil_test.raw_op):
- case PICA_REG_INDEX(framebuffer.depth_format):
+ case PICA_REG_INDEX(framebuffer.output_merger.stencil_test.raw_op):
+ case PICA_REG_INDEX(framebuffer.framebuffer.depth_format):
SyncStencilTest();
break;
// Sync GL depth test + depth and color write mask
// (Pica depth test function register also contains a depth and color write mask)
- case PICA_REG_INDEX(output_merger.depth_test_enable):
+ case PICA_REG_INDEX(framebuffer.output_merger.depth_test_enable):
SyncDepthTest();
SyncDepthWriteMask();
SyncColorWriteMask();
@@ -386,88 +382,88 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
// Sync GL depth and stencil write mask
// (This is a dedicated combined depth / stencil write-enable register)
- case PICA_REG_INDEX(framebuffer.allow_depth_stencil_write):
+ case PICA_REG_INDEX(framebuffer.framebuffer.allow_depth_stencil_write):
SyncDepthWriteMask();
SyncStencilWriteMask();
break;
// Sync GL color write mask
// (This is a dedicated color write-enable register)
- case PICA_REG_INDEX(framebuffer.allow_color_write):
+ case PICA_REG_INDEX(framebuffer.framebuffer.allow_color_write):
SyncColorWriteMask();
break;
// Scissor test
- case PICA_REG_INDEX(scissor_test.mode):
+ case PICA_REG_INDEX(rasterizer.scissor_test.mode):
shader_dirty = true;
break;
// Logic op
- case PICA_REG_INDEX(output_merger.logic_op):
+ case PICA_REG_INDEX(framebuffer.output_merger.logic_op):
SyncLogicOp();
break;
// Texture 0 type
- case PICA_REG_INDEX(texture0.type):
+ case PICA_REG_INDEX(texturing.texture0.type):
shader_dirty = true;
break;
// TEV stages
// (This also syncs fog_mode and fog_flip which are part of tev_combiner_buffer_input)
- case PICA_REG_INDEX(tev_stage0.color_source1):
- case PICA_REG_INDEX(tev_stage0.color_modifier1):
- case PICA_REG_INDEX(tev_stage0.color_op):
- case PICA_REG_INDEX(tev_stage0.color_scale):
- case PICA_REG_INDEX(tev_stage1.color_source1):
- case PICA_REG_INDEX(tev_stage1.color_modifier1):
- case PICA_REG_INDEX(tev_stage1.color_op):
- case PICA_REG_INDEX(tev_stage1.color_scale):
- case PICA_REG_INDEX(tev_stage2.color_source1):
- case PICA_REG_INDEX(tev_stage2.color_modifier1):
- case PICA_REG_INDEX(tev_stage2.color_op):
- case PICA_REG_INDEX(tev_stage2.color_scale):
- case PICA_REG_INDEX(tev_stage3.color_source1):
- case PICA_REG_INDEX(tev_stage3.color_modifier1):
- case PICA_REG_INDEX(tev_stage3.color_op):
- case PICA_REG_INDEX(tev_stage3.color_scale):
- case PICA_REG_INDEX(tev_stage4.color_source1):
- case PICA_REG_INDEX(tev_stage4.color_modifier1):
- case PICA_REG_INDEX(tev_stage4.color_op):
- case PICA_REG_INDEX(tev_stage4.color_scale):
- case PICA_REG_INDEX(tev_stage5.color_source1):
- case PICA_REG_INDEX(tev_stage5.color_modifier1):
- case PICA_REG_INDEX(tev_stage5.color_op):
- case PICA_REG_INDEX(tev_stage5.color_scale):
- case PICA_REG_INDEX(tev_combiner_buffer_input):
+ case PICA_REG_INDEX(texturing.tev_stage0.color_source1):
+ case PICA_REG_INDEX(texturing.tev_stage0.color_modifier1):
+ case PICA_REG_INDEX(texturing.tev_stage0.color_op):
+ case PICA_REG_INDEX(texturing.tev_stage0.color_scale):
+ case PICA_REG_INDEX(texturing.tev_stage1.color_source1):
+ case PICA_REG_INDEX(texturing.tev_stage1.color_modifier1):
+ case PICA_REG_INDEX(texturing.tev_stage1.color_op):
+ case PICA_REG_INDEX(texturing.tev_stage1.color_scale):
+ case PICA_REG_INDEX(texturing.tev_stage2.color_source1):
+ case PICA_REG_INDEX(texturing.tev_stage2.color_modifier1):
+ case PICA_REG_INDEX(texturing.tev_stage2.color_op):
+ case PICA_REG_INDEX(texturing.tev_stage2.color_scale):
+ case PICA_REG_INDEX(texturing.tev_stage3.color_source1):
+ case PICA_REG_INDEX(texturing.tev_stage3.color_modifier1):
+ case PICA_REG_INDEX(texturing.tev_stage3.color_op):
+ case PICA_REG_INDEX(texturing.tev_stage3.color_scale):
+ case PICA_REG_INDEX(texturing.tev_stage4.color_source1):
+ case PICA_REG_INDEX(texturing.tev_stage4.color_modifier1):
+ case PICA_REG_INDEX(texturing.tev_stage4.color_op):
+ case PICA_REG_INDEX(texturing.tev_stage4.color_scale):
+ case PICA_REG_INDEX(texturing.tev_stage5.color_source1):
+ case PICA_REG_INDEX(texturing.tev_stage5.color_modifier1):
+ case PICA_REG_INDEX(texturing.tev_stage5.color_op):
+ case PICA_REG_INDEX(texturing.tev_stage5.color_scale):
+ case PICA_REG_INDEX(texturing.tev_combiner_buffer_input):
shader_dirty = true;
break;
- case PICA_REG_INDEX(tev_stage0.const_r):
- SyncTevConstColor(0, regs.tev_stage0);
+ case PICA_REG_INDEX(texturing.tev_stage0.const_r):
+ SyncTevConstColor(0, regs.texturing.tev_stage0);
break;
- case PICA_REG_INDEX(tev_stage1.const_r):
- SyncTevConstColor(1, regs.tev_stage1);
+ case PICA_REG_INDEX(texturing.tev_stage1.const_r):
+ SyncTevConstColor(1, regs.texturing.tev_stage1);
break;
- case PICA_REG_INDEX(tev_stage2.const_r):
- SyncTevConstColor(2, regs.tev_stage2);
+ case PICA_REG_INDEX(texturing.tev_stage2.const_r):
+ SyncTevConstColor(2, regs.texturing.tev_stage2);
break;
- case PICA_REG_INDEX(tev_stage3.const_r):
- SyncTevConstColor(3, regs.tev_stage3);
+ case PICA_REG_INDEX(texturing.tev_stage3.const_r):
+ SyncTevConstColor(3, regs.texturing.tev_stage3);
break;
- case PICA_REG_INDEX(tev_stage4.const_r):
- SyncTevConstColor(4, regs.tev_stage4);
+ case PICA_REG_INDEX(texturing.tev_stage4.const_r):
+ SyncTevConstColor(4, regs.texturing.tev_stage4);
break;
- case PICA_REG_INDEX(tev_stage5.const_r):
- SyncTevConstColor(5, regs.tev_stage5);
+ case PICA_REG_INDEX(texturing.tev_stage5.const_r):
+ SyncTevConstColor(5, regs.texturing.tev_stage5);
break;
// TEV combiner buffer color
- case PICA_REG_INDEX(tev_combiner_buffer_color):
+ case PICA_REG_INDEX(texturing.tev_combiner_buffer_color):
SyncCombinerColor();
break;
// Fragment lighting switches
case PICA_REG_INDEX(lighting.disable):
- case PICA_REG_INDEX(lighting.num_lights):
+ case PICA_REG_INDEX(lighting.max_light_index):
case PICA_REG_INDEX(lighting.config0):
case PICA_REG_INDEX(lighting.config1):
case PICA_REG_INDEX(lighting.abs_lut_input):
@@ -976,7 +972,9 @@ void RasterizerOpenGL::SamplerInfo::Create() {
// Other attributes have correct defaults
}
-void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Pica::Regs::TextureConfig& config) {
+void RasterizerOpenGL::SamplerInfo::SyncWithConfig(
+ const Pica::TexturingRegs::TextureConfig& config) {
+
GLuint s = sampler.handle;
if (mag_filter != config.mag_filter) {
@@ -1073,67 +1071,69 @@ void RasterizerOpenGL::SetShader() {
current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get();
- unsigned int block_index =
- glGetUniformBlockIndex(current_shader->shader.handle, "shader_data");
- GLint block_size;
- glGetActiveUniformBlockiv(current_shader->shader.handle, block_index,
- GL_UNIFORM_BLOCK_DATA_SIZE, &block_size);
- ASSERT_MSG(block_size == sizeof(UniformData),
- "Uniform block size did not match! Got %d, expected %zu",
- static_cast<int>(block_size), sizeof(UniformData));
- glUniformBlockBinding(current_shader->shader.handle, block_index, 0);
-
- // Update uniforms
- SyncDepthScale();
- SyncDepthOffset();
- SyncAlphaTest();
- SyncCombinerColor();
- auto& tev_stages = Pica::g_state.regs.GetTevStages();
- for (int index = 0; index < tev_stages.size(); ++index)
- SyncTevConstColor(index, tev_stages[index]);
+ GLuint block_index = glGetUniformBlockIndex(current_shader->shader.handle, "shader_data");
+ if (block_index != GL_INVALID_INDEX) {
+ GLint block_size;
+ glGetActiveUniformBlockiv(current_shader->shader.handle, block_index,
+ GL_UNIFORM_BLOCK_DATA_SIZE, &block_size);
+ ASSERT_MSG(block_size == sizeof(UniformData),
+ "Uniform block size did not match! Got %d, expected %zu",
+ static_cast<int>(block_size), sizeof(UniformData));
+ glUniformBlockBinding(current_shader->shader.handle, block_index, 0);
+
+ // Update uniforms
+ SyncDepthScale();
+ SyncDepthOffset();
+ SyncAlphaTest();
+ SyncCombinerColor();
+ auto& tev_stages = Pica::g_state.regs.texturing.GetTevStages();
+ for (int index = 0; index < tev_stages.size(); ++index)
+ SyncTevConstColor(index, tev_stages[index]);
+
+ SyncGlobalAmbient();
+ for (int light_index = 0; light_index < 8; light_index++) {
+ SyncLightSpecular0(light_index);
+ SyncLightSpecular1(light_index);
+ SyncLightDiffuse(light_index);
+ SyncLightAmbient(light_index);
+ SyncLightPosition(light_index);
+ SyncLightDistanceAttenuationBias(light_index);
+ SyncLightDistanceAttenuationScale(light_index);
+ }
- SyncGlobalAmbient();
- for (int light_index = 0; light_index < 8; light_index++) {
- SyncLightSpecular0(light_index);
- SyncLightSpecular1(light_index);
- SyncLightDiffuse(light_index);
- SyncLightAmbient(light_index);
- SyncLightPosition(light_index);
- SyncLightDistanceAttenuationBias(light_index);
- SyncLightDistanceAttenuationScale(light_index);
+ SyncFogColor();
}
-
- SyncFogColor();
}
}
void RasterizerOpenGL::SyncCullMode() {
const auto& regs = Pica::g_state.regs;
- switch (regs.cull_mode) {
- case Pica::Regs::CullMode::KeepAll:
+ switch (regs.rasterizer.cull_mode) {
+ case Pica::RasterizerRegs::CullMode::KeepAll:
state.cull.enabled = false;
break;
- case Pica::Regs::CullMode::KeepClockWise:
+ case Pica::RasterizerRegs::CullMode::KeepClockWise:
state.cull.enabled = true;
state.cull.front_face = GL_CW;
break;
- case Pica::Regs::CullMode::KeepCounterClockWise:
+ case Pica::RasterizerRegs::CullMode::KeepCounterClockWise:
state.cull.enabled = true;
state.cull.front_face = GL_CCW;
break;
default:
- LOG_CRITICAL(Render_OpenGL, "Unknown cull mode %d", regs.cull_mode.Value());
+ LOG_CRITICAL(Render_OpenGL, "Unknown cull mode %d", regs.rasterizer.cull_mode.Value());
UNIMPLEMENTED();
break;
}
}
void RasterizerOpenGL::SyncDepthScale() {
- float depth_scale = Pica::float24::FromRaw(Pica::g_state.regs.viewport_depth_range).ToFloat32();
+ float depth_scale =
+ Pica::float24::FromRaw(Pica::g_state.regs.rasterizer.viewport_depth_range).ToFloat32();
if (depth_scale != uniform_block_data.data.depth_scale) {
uniform_block_data.data.depth_scale = depth_scale;
uniform_block_data.dirty = true;
@@ -1142,7 +1142,7 @@ void RasterizerOpenGL::SyncDepthScale() {
void RasterizerOpenGL::SyncDepthOffset() {
float depth_offset =
- Pica::float24::FromRaw(Pica::g_state.regs.viewport_depth_near_plane).ToFloat32();
+ Pica::float24::FromRaw(Pica::g_state.regs.rasterizer.viewport_depth_near_plane).ToFloat32();
if (depth_offset != uniform_block_data.data.depth_offset) {
uniform_block_data.data.depth_offset = depth_offset;
uniform_block_data.dirty = true;
@@ -1150,25 +1150,28 @@ void RasterizerOpenGL::SyncDepthOffset() {
}
void RasterizerOpenGL::SyncBlendEnabled() {
- state.blend.enabled = (Pica::g_state.regs.output_merger.alphablend_enable == 1);
+ state.blend.enabled = (Pica::g_state.regs.framebuffer.output_merger.alphablend_enable == 1);
}
void RasterizerOpenGL::SyncBlendFuncs() {
const auto& regs = Pica::g_state.regs;
state.blend.rgb_equation =
- PicaToGL::BlendEquation(regs.output_merger.alpha_blending.blend_equation_rgb);
+ PicaToGL::BlendEquation(regs.framebuffer.output_merger.alpha_blending.blend_equation_rgb);
state.blend.a_equation =
- PicaToGL::BlendEquation(regs.output_merger.alpha_blending.blend_equation_a);
+ PicaToGL::BlendEquation(regs.framebuffer.output_merger.alpha_blending.blend_equation_a);
state.blend.src_rgb_func =
- PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_source_rgb);
+ PicaToGL::BlendFunc(regs.framebuffer.output_merger.alpha_blending.factor_source_rgb);
state.blend.dst_rgb_func =
- PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_dest_rgb);
- state.blend.src_a_func = PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_source_a);
- state.blend.dst_a_func = PicaToGL::BlendFunc(regs.output_merger.alpha_blending.factor_dest_a);
+ PicaToGL::BlendFunc(regs.framebuffer.output_merger.alpha_blending.factor_dest_rgb);
+ state.blend.src_a_func =
+ PicaToGL::BlendFunc(regs.framebuffer.output_merger.alpha_blending.factor_source_a);
+ state.blend.dst_a_func =
+ PicaToGL::BlendFunc(regs.framebuffer.output_merger.alpha_blending.factor_dest_a);
}
void RasterizerOpenGL::SyncBlendColor() {
- auto blend_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.output_merger.blend_const.raw);
+ auto blend_color =
+ PicaToGL::ColorRGBA8(Pica::g_state.regs.framebuffer.output_merger.blend_const.raw);
state.blend.color.red = blend_color[0];
state.blend.color.green = blend_color[1];
state.blend.color.blue = blend_color[2];
@@ -1178,8 +1181,8 @@ void RasterizerOpenGL::SyncBlendColor() {
void RasterizerOpenGL::SyncFogColor() {
const auto& regs = Pica::g_state.regs;
uniform_block_data.data.fog_color = {
- regs.fog_color.r.Value() / 255.0f, regs.fog_color.g.Value() / 255.0f,
- regs.fog_color.b.Value() / 255.0f,
+ regs.texturing.fog_color.r.Value() / 255.0f, regs.texturing.fog_color.g.Value() / 255.0f,
+ regs.texturing.fog_color.b.Value() / 255.0f,
};
uniform_block_data.dirty = true;
}
@@ -1200,70 +1203,78 @@ void RasterizerOpenGL::SyncFogLUT() {
void RasterizerOpenGL::SyncAlphaTest() {
const auto& regs = Pica::g_state.regs;
- if (regs.output_merger.alpha_test.ref != uniform_block_data.data.alphatest_ref) {
- uniform_block_data.data.alphatest_ref = regs.output_merger.alpha_test.ref;
+ if (regs.framebuffer.output_merger.alpha_test.ref != uniform_block_data.data.alphatest_ref) {
+ uniform_block_data.data.alphatest_ref = regs.framebuffer.output_merger.alpha_test.ref;
uniform_block_data.dirty = true;
}
}
void RasterizerOpenGL::SyncLogicOp() {
- state.logic_op = PicaToGL::LogicOp(Pica::g_state.regs.output_merger.logic_op);
+ state.logic_op = PicaToGL::LogicOp(Pica::g_state.regs.framebuffer.output_merger.logic_op);
}
void RasterizerOpenGL::SyncColorWriteMask() {
const auto& regs = Pica::g_state.regs;
auto IsColorWriteEnabled = [&](u32 value) {
- return (regs.framebuffer.allow_color_write != 0 && value != 0) ? GL_TRUE : GL_FALSE;
+ return (regs.framebuffer.framebuffer.allow_color_write != 0 && value != 0) ? GL_TRUE
+ : GL_FALSE;
};
- state.color_mask.red_enabled = IsColorWriteEnabled(regs.output_merger.red_enable);
- state.color_mask.green_enabled = IsColorWriteEnabled(regs.output_merger.green_enable);
- state.color_mask.blue_enabled = IsColorWriteEnabled(regs.output_merger.blue_enable);
- state.color_mask.alpha_enabled = IsColorWriteEnabled(regs.output_merger.alpha_enable);
+ state.color_mask.red_enabled = IsColorWriteEnabled(regs.framebuffer.output_merger.red_enable);
+ state.color_mask.green_enabled =
+ IsColorWriteEnabled(regs.framebuffer.output_merger.green_enable);
+ state.color_mask.blue_enabled = IsColorWriteEnabled(regs.framebuffer.output_merger.blue_enable);
+ state.color_mask.alpha_enabled =
+ IsColorWriteEnabled(regs.framebuffer.output_merger.alpha_enable);
}
void RasterizerOpenGL::SyncStencilWriteMask() {
const auto& regs = Pica::g_state.regs;
- state.stencil.write_mask = (regs.framebuffer.allow_depth_stencil_write != 0)
- ? static_cast<GLuint>(regs.output_merger.stencil_test.write_mask)
- : 0;
+ state.stencil.write_mask =
+ (regs.framebuffer.framebuffer.allow_depth_stencil_write != 0)
+ ? static_cast<GLuint>(regs.framebuffer.output_merger.stencil_test.write_mask)
+ : 0;
}
void RasterizerOpenGL::SyncDepthWriteMask() {
const auto& regs = Pica::g_state.regs;
- state.depth.write_mask =
- (regs.framebuffer.allow_depth_stencil_write != 0 && regs.output_merger.depth_write_enable)
- ? GL_TRUE
- : GL_FALSE;
+ state.depth.write_mask = (regs.framebuffer.framebuffer.allow_depth_stencil_write != 0 &&
+ regs.framebuffer.output_merger.depth_write_enable)
+ ? GL_TRUE
+ : GL_FALSE;
}
void RasterizerOpenGL::SyncStencilTest() {
const auto& regs = Pica::g_state.regs;
- state.stencil.test_enabled = regs.output_merger.stencil_test.enable &&
- regs.framebuffer.depth_format == Pica::Regs::DepthFormat::D24S8;
- state.stencil.test_func = PicaToGL::CompareFunc(regs.output_merger.stencil_test.func);
- state.stencil.test_ref = regs.output_merger.stencil_test.reference_value;
- state.stencil.test_mask = regs.output_merger.stencil_test.input_mask;
+ state.stencil.test_enabled =
+ regs.framebuffer.output_merger.stencil_test.enable &&
+ regs.framebuffer.framebuffer.depth_format == Pica::FramebufferRegs::DepthFormat::D24S8;
+ state.stencil.test_func =
+ PicaToGL::CompareFunc(regs.framebuffer.output_merger.stencil_test.func);
+ state.stencil.test_ref = regs.framebuffer.output_merger.stencil_test.reference_value;
+ state.stencil.test_mask = regs.framebuffer.output_merger.stencil_test.input_mask;
state.stencil.action_stencil_fail =
- PicaToGL::StencilOp(regs.output_merger.stencil_test.action_stencil_fail);
+ PicaToGL::StencilOp(regs.framebuffer.output_merger.stencil_test.action_stencil_fail);
state.stencil.action_depth_fail =
- PicaToGL::StencilOp(regs.output_merger.stencil_test.action_depth_fail);
+ PicaToGL::StencilOp(regs.framebuffer.output_merger.stencil_test.action_depth_fail);
state.stencil.action_depth_pass =
- PicaToGL::StencilOp(regs.output_merger.stencil_test.action_depth_pass);
+ PicaToGL::StencilOp(regs.framebuffer.output_merger.stencil_test.action_depth_pass);
}
void RasterizerOpenGL::SyncDepthTest() {
const auto& regs = Pica::g_state.regs;
- state.depth.test_enabled =
- regs.output_merger.depth_test_enable == 1 || regs.output_merger.depth_write_enable == 1;
- state.depth.test_func = regs.output_merger.depth_test_enable == 1
- ? PicaToGL::CompareFunc(regs.output_merger.depth_test_func)
- : GL_ALWAYS;
+ state.depth.test_enabled = regs.framebuffer.output_merger.depth_test_enable == 1 ||
+ regs.framebuffer.output_merger.depth_write_enable == 1;
+ state.depth.test_func =
+ regs.framebuffer.output_merger.depth_test_enable == 1
+ ? PicaToGL::CompareFunc(regs.framebuffer.output_merger.depth_test_func)
+ : GL_ALWAYS;
}
void RasterizerOpenGL::SyncCombinerColor() {
- auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw);
+ auto combiner_color =
+ PicaToGL::ColorRGBA8(Pica::g_state.regs.texturing.tev_combiner_buffer_color.raw);
if (combiner_color != uniform_block_data.data.tev_combiner_buffer_color) {
uniform_block_data.data.tev_combiner_buffer_color = combiner_color;
uniform_block_data.dirty = true;
@@ -1271,7 +1282,7 @@ void RasterizerOpenGL::SyncCombinerColor() {
}
void RasterizerOpenGL::SyncTevConstColor(int stage_index,
- const Pica::Regs::TevStageConfig& tev_stage) {
+ const Pica::TexturingRegs::TevStageConfig& tev_stage) {
auto const_color = PicaToGL::ColorRGBA8(tev_stage.const_color);
if (const_color != uniform_block_data.data.const_color[stage_index]) {
uniform_block_data.data.const_color[stage_index] = const_color;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index cc3e4bed5..ecf737438 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -16,10 +16,13 @@
#include "common/hash.h"
#include "common/vector_math.h"
#include "core/hw/gpu.h"
-#include "video_core/pica.h"
#include "video_core/pica_state.h"
#include "video_core/pica_types.h"
#include "video_core/rasterizer_interface.h"
+#include "video_core/regs_framebuffer.h"
+#include "video_core/regs_lighting.h"
+#include "video_core/regs_rasterizer.h"
+#include "video_core/regs_texturing.h"
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_state.h"
@@ -52,20 +55,20 @@ union PicaShaderConfig {
const auto& regs = Pica::g_state.regs;
- state.scissor_test_mode = regs.scissor_test.mode;
+ state.scissor_test_mode = regs.rasterizer.scissor_test.mode;
- state.depthmap_enable = regs.depthmap_enable;
+ state.depthmap_enable = regs.rasterizer.depthmap_enable;
- state.alpha_test_func = regs.output_merger.alpha_test.enable
- ? regs.output_merger.alpha_test.func.Value()
- : Pica::Regs::CompareFunc::Always;
+ state.alpha_test_func = regs.framebuffer.output_merger.alpha_test.enable
+ ? regs.framebuffer.output_merger.alpha_test.func.Value()
+ : Pica::FramebufferRegs::CompareFunc::Always;
- state.texture0_type = regs.texture0.type;
+ state.texture0_type = regs.texturing.texture0.type;
// Copy relevant tev stages fields.
// We don't sync const_color here because of the high variance, it is a
// shader uniform instead.
- const auto& tev_stages = regs.GetTevStages();
+ const auto& tev_stages = regs.texturing.GetTevStages();
DEBUG_ASSERT(state.tev_stages.size() == tev_stages.size());
for (size_t i = 0; i < tev_stages.size(); i++) {
const auto& tev_stage = tev_stages[i];
@@ -75,16 +78,17 @@ union PicaShaderConfig {
state.tev_stages[i].scales_raw = tev_stage.scales_raw;
}
- state.fog_mode = regs.fog_mode;
- state.fog_flip = regs.fog_flip != 0;
+ state.fog_mode = regs.texturing.fog_mode;
+ state.fog_flip = regs.texturing.fog_flip != 0;
- state.combiner_buffer_input = regs.tev_combiner_buffer_input.update_mask_rgb.Value() |
- regs.tev_combiner_buffer_input.update_mask_a.Value() << 4;
+ state.combiner_buffer_input =
+ regs.texturing.tev_combiner_buffer_input.update_mask_rgb.Value() |
+ regs.texturing.tev_combiner_buffer_input.update_mask_a.Value() << 4;
// Fragment lighting
state.lighting.enable = !regs.lighting.disable;
- state.lighting.src_num = regs.lighting.num_lights + 1;
+ state.lighting.src_num = regs.lighting.max_light_index + 1;
for (unsigned light_index = 0; light_index < state.lighting.src_num; ++light_index) {
unsigned num = regs.lighting.light_enable.GetNum(light_index);
@@ -159,8 +163,8 @@ union PicaShaderConfig {
u32 modifiers_raw;
u32 ops_raw;
u32 scales_raw;
- explicit operator Pica::Regs::TevStageConfig() const noexcept {
- Pica::Regs::TevStageConfig stage;
+ explicit operator Pica::TexturingRegs::TevStageConfig() const noexcept {
+ Pica::TexturingRegs::TevStageConfig stage;
stage.sources_raw = sources_raw;
stage.modifiers_raw = modifiers_raw;
stage.ops_raw = ops_raw;
@@ -171,14 +175,14 @@ union PicaShaderConfig {
};
struct State {
- Pica::Regs::CompareFunc alpha_test_func;
- Pica::Regs::ScissorMode scissor_test_mode;
- Pica::Regs::TextureConfig::TextureType texture0_type;
+ Pica::FramebufferRegs::CompareFunc alpha_test_func;
+ Pica::RasterizerRegs::ScissorMode scissor_test_mode;
+ Pica::TexturingRegs::TextureConfig::TextureType texture0_type;
std::array<TevStageConfigRaw, 6> tev_stages;
u8 combiner_buffer_input;
- Pica::Regs::DepthBuffering depthmap_enable;
- Pica::Regs::FogMode fog_mode;
+ Pica::RasterizerRegs::DepthBuffering depthmap_enable;
+ Pica::TexturingRegs::FogMode fog_mode;
bool fog_flip;
struct {
@@ -191,18 +195,18 @@ union PicaShaderConfig {
bool enable;
unsigned src_num;
- Pica::Regs::LightingBumpMode bump_mode;
+ Pica::LightingRegs::LightingBumpMode bump_mode;
unsigned bump_selector;
bool bump_renorm;
bool clamp_highlights;
- Pica::Regs::LightingConfig config;
- Pica::Regs::LightingFresnelSelector fresnel_selector;
+ Pica::LightingRegs::LightingConfig config;
+ Pica::LightingRegs::LightingFresnelSelector fresnel_selector;
struct {
bool enable;
bool abs_input;
- Pica::Regs::LightingLutInput type;
+ Pica::LightingRegs::LightingLutInput type;
float scale;
} lut_d0, lut_d1, lut_fr, lut_rr, lut_rg, lut_rb;
} lighting;
@@ -251,7 +255,7 @@ public:
private:
struct SamplerInfo {
- using TextureConfig = Pica::Regs::TextureConfig;
+ using TextureConfig = Pica::TexturingRegs::TextureConfig;
OGLSampler sampler;
@@ -398,7 +402,7 @@ private:
void SyncCombinerColor();
/// Syncs the TEV constant color to match the PICA register
- void SyncTevConstColor(int tev_index, const Pica::Regs::TevStageConfig& tev_stage);
+ void SyncTevConstColor(int tev_index, const Pica::TexturingRegs::TevStageConfig& tev_stage);
/// Syncs the lighting global ambient color to match the PICA register
void SyncGlobalAmbient();
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 1e7eedecb..456443e86 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -17,10 +17,11 @@
#include "common/vector_math.h"
#include "core/frontend/emu_window.h"
#include "core/memory.h"
-#include "video_core/debug_utils/debug_utils.h"
+#include "core/settings.h"
#include "video_core/pica_state.h"
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
#include "video_core/renderer_opengl/gl_state.h"
+#include "video_core/texture/texture_decode.h"
#include "video_core/utils.h"
#include "video_core/video_core.h"
@@ -339,17 +340,16 @@ CachedSurface* RasterizerCacheOpenGL::GetSurface(const CachedSurface& params, bo
std::vector<Math::Vec4<u8>> tex_buffer(params.width * params.height);
- Pica::DebugUtils::TextureInfo tex_info;
+ Pica::Texture::TextureInfo tex_info;
tex_info.width = params.width;
tex_info.height = params.height;
- tex_info.stride =
- params.width * CachedSurface::GetFormatBpp(params.pixel_format) / 8;
- tex_info.format = (Pica::Regs::TextureFormat)params.pixel_format;
+ tex_info.format = (Pica::TexturingRegs::TextureFormat)params.pixel_format;
+ tex_info.SetDefaultStride();
tex_info.physical_address = params.addr;
for (unsigned y = 0; y < params.height; ++y) {
for (unsigned x = 0; x < params.width; ++x) {
- tex_buffer[x + params.width * y] = Pica::DebugUtils::LookupTexture(
+ tex_buffer[x + params.width * y] = Pica::Texture::LookupTexture(
texture_src_data, x, params.height - 1 - y, tex_info);
}
}
@@ -511,9 +511,10 @@ CachedSurface* RasterizerCacheOpenGL::GetSurfaceRect(const CachedSurface& params
}
CachedSurface* RasterizerCacheOpenGL::GetTextureSurface(
- const Pica::Regs::FullTextureConfig& config) {
- Pica::DebugUtils::TextureInfo info =
- Pica::DebugUtils::TextureInfo::FromPicaRegister(config.config, config.format);
+ const Pica::TexturingRegs::FullTextureConfig& config) {
+
+ Pica::Texture::TextureInfo info =
+ Pica::Texture::TextureInfo::FromPicaRegister(config.config, config.format);
CachedSurface params;
params.addr = info.physical_address;
@@ -525,7 +526,9 @@ CachedSurface* RasterizerCacheOpenGL::GetTextureSurface(
}
std::tuple<CachedSurface*, CachedSurface*, MathUtil::Rectangle<int>>
-RasterizerCacheOpenGL::GetFramebufferSurfaces(const Pica::Regs::FramebufferConfig& config) {
+RasterizerCacheOpenGL::GetFramebufferSurfaces(
+ const Pica::FramebufferRegs::FramebufferConfig& config) {
+
const auto& regs = Pica::g_state.regs;
// Make sur that framebuffers don't overlap if both color and depth are being used
@@ -537,11 +540,12 @@ RasterizerCacheOpenGL::GetFramebufferSurfaces(const Pica::Regs::FramebufferConfi
config.GetColorBufferPhysicalAddress(),
fb_area * GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(config.color_format.Value())),
config.GetDepthBufferPhysicalAddress(),
- fb_area * Pica::Regs::BytesPerDepthPixel(config.depth_format));
+ fb_area * Pica::FramebufferRegs::BytesPerDepthPixel(config.depth_format));
bool using_color_fb = config.GetColorBufferPhysicalAddress() != 0;
- bool using_depth_fb = config.GetDepthBufferPhysicalAddress() != 0 &&
- (regs.output_merger.depth_test_enable ||
- regs.output_merger.depth_write_enable || !framebuffers_overlap);
+ bool using_depth_fb =
+ config.GetDepthBufferPhysicalAddress() != 0 &&
+ (regs.framebuffer.output_merger.depth_test_enable ||
+ regs.framebuffer.output_merger.depth_write_enable || !framebuffers_overlap);
if (framebuffers_overlap && using_color_fb && using_depth_fb) {
LOG_CRITICAL(Render_OpenGL, "Color and depth framebuffer memory regions overlap; "
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index f57fdb3cc..aea20c693 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -21,7 +21,8 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/hw/gpu.h"
-#include "video_core/pica.h"
+#include "video_core/regs_framebuffer.h"
+#include "video_core/regs_texturing.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
namespace MathUtil {
@@ -96,15 +97,15 @@ struct CachedSurface {
return bpp_table[(unsigned int)format];
}
- static PixelFormat PixelFormatFromTextureFormat(Pica::Regs::TextureFormat format) {
+ static PixelFormat PixelFormatFromTextureFormat(Pica::TexturingRegs::TextureFormat format) {
return ((unsigned int)format < 14) ? (PixelFormat)format : PixelFormat::Invalid;
}
- static PixelFormat PixelFormatFromColorFormat(Pica::Regs::ColorFormat format) {
+ static PixelFormat PixelFormatFromColorFormat(Pica::FramebufferRegs::ColorFormat format) {
return ((unsigned int)format < 5) ? (PixelFormat)format : PixelFormat::Invalid;
}
- static PixelFormat PixelFormatFromDepthFormat(Pica::Regs::DepthFormat format) {
+ static PixelFormat PixelFormatFromDepthFormat(Pica::FramebufferRegs::DepthFormat format) {
return ((unsigned int)format < 4) ? (PixelFormat)((unsigned int)format + 14)
: PixelFormat::Invalid;
}
@@ -212,12 +213,12 @@ public:
bool load_if_create, MathUtil::Rectangle<int>& out_rect);
/// Gets a surface based on the texture configuration
- CachedSurface* GetTextureSurface(const Pica::Regs::FullTextureConfig& config);
+ CachedSurface* GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config);
/// Gets the color and depth surfaces and rect (resolution scaled) based on the framebuffer
/// configuration
std::tuple<CachedSurface*, CachedSurface*, MathUtil::Rectangle<int>> GetFramebufferSurfaces(
- const Pica::Regs::FramebufferConfig& config);
+ const Pica::FramebufferRegs::FramebufferConfig& config);
/// Attempt to get a surface that exactly matches the fill region and format
CachedSurface* TryGetFillSurface(const GPU::Regs::MemoryFillConfig& config);
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index 4c4f98ac9..7abdeba05 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -7,13 +7,19 @@
#include "common/assert.h"
#include "common/bit_field.h"
#include "common/logging/log.h"
-#include "video_core/pica.h"
+#include "video_core/regs_framebuffer.h"
+#include "video_core/regs_lighting.h"
+#include "video_core/regs_rasterizer.h"
+#include "video_core/regs_texturing.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_gen.h"
#include "video_core/renderer_opengl/gl_shader_util.h"
-using Pica::Regs;
-using TevStageConfig = Regs::TevStageConfig;
+using Pica::FramebufferRegs;
+using Pica::LightingRegs;
+using Pica::RasterizerRegs;
+using Pica::TexturingRegs;
+using TevStageConfig = TexturingRegs::TevStageConfig;
namespace GLShader {
@@ -46,10 +52,10 @@ static void AppendSource(std::string& out, const PicaShaderConfig& config,
case Source::Texture0:
// Only unit 0 respects the texturing type (according to 3DBrew)
switch (state.texture0_type) {
- case Pica::Regs::TextureConfig::Texture2D:
+ case TexturingRegs::TextureConfig::Texture2D:
out += "texture(tex[0], texcoord[0])";
break;
- case Pica::Regs::TextureConfig::Projection2D:
+ case TexturingRegs::TextureConfig::Projection2D:
out += "textureProj(tex[0], vec3(texcoord[0], texcoord0_w))";
break;
default:
@@ -276,8 +282,8 @@ static void AppendAlphaCombiner(std::string& out, TevStageConfig::Operation oper
}
/// Writes the if-statement condition used to evaluate alpha testing
-static void AppendAlphaTestCondition(std::string& out, Regs::CompareFunc func) {
- using CompareFunc = Regs::CompareFunc;
+static void AppendAlphaTestCondition(std::string& out, FramebufferRegs::CompareFunc func) {
+ using CompareFunc = FramebufferRegs::CompareFunc;
switch (func) {
case CompareFunc::Never:
out += "true";
@@ -307,7 +313,7 @@ static void AppendAlphaTestCondition(std::string& out, Regs::CompareFunc func) {
/// Writes the code to emulate the specified TEV stage
static void WriteTevStage(std::string& out, const PicaShaderConfig& config, unsigned index) {
const auto stage =
- static_cast<const Pica::Regs::TevStageConfig>(config.state.tev_stages[index]);
+ static_cast<const TexturingRegs::TevStageConfig>(config.state.tev_stages[index]);
if (!IsPassThroughTevStage(stage)) {
std::string index_name = std::to_string(index);
@@ -364,7 +370,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
"vec3 refl_value = vec3(0.0);\n";
// Compute fragment normals
- if (lighting.bump_mode == Pica::Regs::LightingBumpMode::NormalMap) {
+ if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) {
// Bump mapping is enabled using a normal map, read perturbation vector from the selected
// texture
std::string bump_selector = std::to_string(lighting.bump_selector);
@@ -378,7 +384,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
"(1.0 - (surface_normal.x*surface_normal.x + surface_normal.y*surface_normal.y))";
out += "surface_normal.z = sqrt(max(" + val + ", 0.0));\n";
}
- } else if (lighting.bump_mode == Pica::Regs::LightingBumpMode::TangentMap) {
+ } else if (lighting.bump_mode == LightingRegs::LightingBumpMode::TangentMap) {
// Bump mapping is enabled using a tangent map
LOG_CRITICAL(HW_GPU, "unimplemented bump mapping mode (tangent mapping)");
UNIMPLEMENTED();
@@ -392,23 +398,24 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
out += "vec3 normal = normalize(quaternion_rotate(normquat, surface_normal));\n";
// Gets the index into the specified lookup table for specular lighting
- auto GetLutIndex = [&lighting](unsigned light_num, Regs::LightingLutInput input, bool abs) {
+ auto GetLutIndex = [&lighting](unsigned light_num, LightingRegs::LightingLutInput input,
+ bool abs) {
const std::string half_angle = "normalize(normalize(view) + light_vector)";
std::string index;
switch (input) {
- case Regs::LightingLutInput::NH:
+ case LightingRegs::LightingLutInput::NH:
index = "dot(normal, " + half_angle + ")";
break;
- case Regs::LightingLutInput::VH:
+ case LightingRegs::LightingLutInput::VH:
index = std::string("dot(normalize(view), " + half_angle + ")");
break;
- case Regs::LightingLutInput::NV:
+ case LightingRegs::LightingLutInput::NV:
index = std::string("dot(normal, normalize(view))");
break;
- case Regs::LightingLutInput::LN:
+ case LightingRegs::LightingLutInput::LN:
index = std::string("dot(light_vector, normal)");
break;
@@ -432,7 +439,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
};
// Gets the lighting lookup table value given the specified sampler and index
- auto GetLutValue = [](Regs::LightingSampler sampler, std::string lut_index) {
+ auto GetLutValue = [](LightingRegs::LightingSampler sampler, std::string lut_index) {
return std::string("texture(lut[" + std::to_string((unsigned)sampler / 4) + "], " +
lut_index + ")[" + std::to_string((unsigned)sampler & 3) + "]");
};
@@ -461,8 +468,8 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
light_src + ".position) + " + light_src + ".dist_atten_bias)";
index = "(OFFSET_256 + SCALE_256 * clamp(" + index + ", 0.0, 1.0))";
const unsigned lut_num =
- ((unsigned)Regs::LightingSampler::DistanceAttenuation + light_config.num);
- dist_atten = GetLutValue((Regs::LightingSampler)lut_num, index);
+ ((unsigned)LightingRegs::LightingSampler::DistanceAttenuation + light_config.num);
+ dist_atten = GetLutValue((LightingRegs::LightingSampler)lut_num, index);
}
// If enabled, clamp specular component if lighting result is negative
@@ -472,24 +479,24 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
// Specular 0 component
std::string d0_lut_value = "1.0";
if (lighting.lut_d0.enable &&
- Pica::Regs::IsLightingSamplerSupported(lighting.config,
- Pica::Regs::LightingSampler::Distribution0)) {
+ LightingRegs::IsLightingSamplerSupported(
+ lighting.config, LightingRegs::LightingSampler::Distribution0)) {
// Lookup specular "distribution 0" LUT value
std::string index =
GetLutIndex(light_config.num, lighting.lut_d0.type, lighting.lut_d0.abs_input);
d0_lut_value = "(" + std::to_string(lighting.lut_d0.scale) + " * " +
- GetLutValue(Regs::LightingSampler::Distribution0, index) + ")";
+ GetLutValue(LightingRegs::LightingSampler::Distribution0, index) + ")";
}
std::string specular_0 = "(" + d0_lut_value + " * " + light_src + ".specular_0)";
// If enabled, lookup ReflectRed value, otherwise, 1.0 is used
if (lighting.lut_rr.enable &&
- Pica::Regs::IsLightingSamplerSupported(lighting.config,
- Pica::Regs::LightingSampler::ReflectRed)) {
+ LightingRegs::IsLightingSamplerSupported(lighting.config,
+ LightingRegs::LightingSampler::ReflectRed)) {
std::string index =
GetLutIndex(light_config.num, lighting.lut_rr.type, lighting.lut_rr.abs_input);
std::string value = "(" + std::to_string(lighting.lut_rr.scale) + " * " +
- GetLutValue(Regs::LightingSampler::ReflectRed, index) + ")";
+ GetLutValue(LightingRegs::LightingSampler::ReflectRed, index) + ")";
out += "refl_value.r = " + value + ";\n";
} else {
out += "refl_value.r = 1.0;\n";
@@ -497,12 +504,13 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
// If enabled, lookup ReflectGreen value, otherwise, ReflectRed value is used
if (lighting.lut_rg.enable &&
- Pica::Regs::IsLightingSamplerSupported(lighting.config,
- Pica::Regs::LightingSampler::ReflectGreen)) {
+ LightingRegs::IsLightingSamplerSupported(lighting.config,
+ LightingRegs::LightingSampler::ReflectGreen)) {
std::string index =
GetLutIndex(light_config.num, lighting.lut_rg.type, lighting.lut_rg.abs_input);
std::string value = "(" + std::to_string(lighting.lut_rg.scale) + " * " +
- GetLutValue(Regs::LightingSampler::ReflectGreen, index) + ")";
+ GetLutValue(LightingRegs::LightingSampler::ReflectGreen, index) +
+ ")";
out += "refl_value.g = " + value + ";\n";
} else {
out += "refl_value.g = refl_value.r;\n";
@@ -510,12 +518,13 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
// If enabled, lookup ReflectBlue value, otherwise, ReflectRed value is used
if (lighting.lut_rb.enable &&
- Pica::Regs::IsLightingSamplerSupported(lighting.config,
- Pica::Regs::LightingSampler::ReflectBlue)) {
+ LightingRegs::IsLightingSamplerSupported(lighting.config,
+ LightingRegs::LightingSampler::ReflectBlue)) {
std::string index =
GetLutIndex(light_config.num, lighting.lut_rb.type, lighting.lut_rb.abs_input);
std::string value = "(" + std::to_string(lighting.lut_rb.scale) + " * " +
- GetLutValue(Regs::LightingSampler::ReflectBlue, index) + ")";
+ GetLutValue(LightingRegs::LightingSampler::ReflectBlue, index) +
+ ")";
out += "refl_value.b = " + value + ";\n";
} else {
out += "refl_value.b = refl_value.r;\n";
@@ -524,35 +533,39 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) {
// Specular 1 component
std::string d1_lut_value = "1.0";
if (lighting.lut_d1.enable &&
- Pica::Regs::IsLightingSamplerSupported(lighting.config,
- Pica::Regs::LightingSampler::Distribution1)) {
+ LightingRegs::IsLightingSamplerSupported(
+ lighting.config, LightingRegs::LightingSampler::Distribution1)) {
// Lookup specular "distribution 1" LUT value
std::string index =
GetLutIndex(light_config.num, lighting.lut_d1.type, lighting.lut_d1.abs_input);
d1_lut_value = "(" + std::to_string(lighting.lut_d1.scale) + " * " +
- GetLutValue(Regs::LightingSampler::Distribution1, index) + ")";
+ GetLutValue(LightingRegs::LightingSampler::Distribution1, index) + ")";
}
std::string specular_1 =
"(" + d1_lut_value + " * refl_value * " + light_src + ".specular_1)";
// Fresnel
- if (lighting.lut_fr.enable && Pica::Regs::IsLightingSamplerSupported(
- lighting.config, Pica::Regs::LightingSampler::Fresnel)) {
+ if (lighting.lut_fr.enable &&
+ LightingRegs::IsLightingSamplerSupported(lighting.config,
+ LightingRegs::LightingSampler::Fresnel)) {
// Lookup fresnel LUT value
std::string index =
GetLutIndex(light_config.num, lighting.lut_fr.type, lighting.lut_fr.abs_input);
std::string value = "(" + std::to_string(lighting.lut_fr.scale) + " * " +
- GetLutValue(Regs::LightingSampler::Fresnel, index) + ")";
+ GetLutValue(LightingRegs::LightingSampler::Fresnel, index) + ")";
// Enabled for difffuse lighting alpha component
- if (lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::PrimaryAlpha ||
- lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::Both)
+ if (lighting.fresnel_selector == LightingRegs::LightingFresnelSelector::PrimaryAlpha ||
+ lighting.fresnel_selector == LightingRegs::LightingFresnelSelector::Both) {
out += "diffuse_sum.a *= " + value + ";\n";
+ }
// Enabled for the specular lighting alpha component
- if (lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::SecondaryAlpha ||
- lighting.fresnel_selector == Pica::Regs::LightingFresnelSelector::Both)
+ if (lighting.fresnel_selector ==
+ LightingRegs::LightingFresnelSelector::SecondaryAlpha ||
+ lighting.fresnel_selector == LightingRegs::LightingFresnelSelector::Both) {
out += "specular_sum.a *= " + value + ";\n";
+ }
}
// Compute primary fragment color (diffuse lighting) function
@@ -633,16 +646,16 @@ vec4 secondary_fragment_color = vec4(0.0);
)";
// Do not do any sort of processing if it's obvious we're not going to pass the alpha test
- if (state.alpha_test_func == Regs::CompareFunc::Never) {
+ if (state.alpha_test_func == FramebufferRegs::CompareFunc::Never) {
out += "discard; }";
return out;
}
// Append the scissor test
- if (state.scissor_test_mode != Regs::ScissorMode::Disabled) {
+ if (state.scissor_test_mode != RasterizerRegs::ScissorMode::Disabled) {
out += "if (";
// Negate the condition if we have to keep only the pixels outside the scissor box
- if (state.scissor_test_mode == Regs::ScissorMode::Include)
+ if (state.scissor_test_mode == RasterizerRegs::ScissorMode::Include)
out += "!";
out += "(gl_FragCoord.x >= scissor_x1 && "
"gl_FragCoord.y >= scissor_y1 && "
@@ -652,7 +665,7 @@ vec4 secondary_fragment_color = vec4(0.0);
out += "float z_over_w = 1.0 - gl_FragCoord.z * 2.0;\n";
out += "float depth = z_over_w * depth_scale + depth_offset;\n";
- if (state.depthmap_enable == Pica::Regs::DepthBuffering::WBuffering) {
+ if (state.depthmap_enable == RasterizerRegs::DepthBuffering::WBuffering) {
out += "depth /= gl_FragCoord.w;\n";
}
@@ -666,14 +679,14 @@ vec4 secondary_fragment_color = vec4(0.0);
for (size_t index = 0; index < state.tev_stages.size(); ++index)
WriteTevStage(out, config, (unsigned)index);
- if (state.alpha_test_func != Regs::CompareFunc::Always) {
+ if (state.alpha_test_func != FramebufferRegs::CompareFunc::Always) {
out += "if (";
AppendAlphaTestCondition(out, state.alpha_test_func);
out += ") discard;\n";
}
// Append fog combiner
- if (state.fog_mode == Regs::FogMode::Fog) {
+ if (state.fog_mode == TexturingRegs::FogMode::Fog) {
// Get index into fog LUT
if (state.fog_flip) {
out += "float fog_index = (1.0 - depth) * 128.0;\n";
diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h
index cc49867c8..93d7b0b71 100644
--- a/src/video_core/renderer_opengl/pica_to_gl.h
+++ b/src/video_core/renderer_opengl/pica_to_gl.h
@@ -12,7 +12,9 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/logging/log.h"
-#include "video_core/pica.h"
+#include "video_core/regs_framebuffer.h"
+#include "video_core/regs_lighting.h"
+#include "video_core/regs_texturing.h"
using GLvec2 = std::array<GLfloat, 2>;
using GLvec3 = std::array<GLfloat, 3>;
@@ -20,7 +22,7 @@ using GLvec4 = std::array<GLfloat, 4>;
namespace PicaToGL {
-inline GLenum TextureFilterMode(Pica::Regs::TextureConfig::TextureFilter mode) {
+inline GLenum TextureFilterMode(Pica::TexturingRegs::TextureConfig::TextureFilter mode) {
static const GLenum filter_mode_table[] = {
GL_NEAREST, // TextureFilter::Nearest
GL_LINEAR, // TextureFilter::Linear
@@ -47,7 +49,7 @@ inline GLenum TextureFilterMode(Pica::Regs::TextureConfig::TextureFilter mode) {
return gl_mode;
}
-inline GLenum WrapMode(Pica::Regs::TextureConfig::WrapMode mode) {
+inline GLenum WrapMode(Pica::TexturingRegs::TextureConfig::WrapMode mode) {
static const GLenum wrap_mode_table[] = {
GL_CLAMP_TO_EDGE, // WrapMode::ClampToEdge
GL_CLAMP_TO_BORDER, // WrapMode::ClampToBorder
@@ -76,7 +78,7 @@ inline GLenum WrapMode(Pica::Regs::TextureConfig::WrapMode mode) {
return gl_mode;
}
-inline GLenum BlendEquation(Pica::Regs::BlendEquation equation) {
+inline GLenum BlendEquation(Pica::FramebufferRegs::BlendEquation equation) {
static const GLenum blend_equation_table[] = {
GL_FUNC_ADD, // BlendEquation::Add
GL_FUNC_SUBTRACT, // BlendEquation::Subtract
@@ -96,7 +98,7 @@ inline GLenum BlendEquation(Pica::Regs::BlendEquation equation) {
return blend_equation_table[(unsigned)equation];
}
-inline GLenum BlendFunc(Pica::Regs::BlendFactor factor) {
+inline GLenum BlendFunc(Pica::FramebufferRegs::BlendFactor factor) {
static const GLenum blend_func_table[] = {
GL_ZERO, // BlendFactor::Zero
GL_ONE, // BlendFactor::One
@@ -126,7 +128,7 @@ inline GLenum BlendFunc(Pica::Regs::BlendFactor factor) {
return blend_func_table[(unsigned)factor];
}
-inline GLenum LogicOp(Pica::Regs::LogicOp op) {
+inline GLenum LogicOp(Pica::FramebufferRegs::LogicOp op) {
static const GLenum logic_op_table[] = {
GL_CLEAR, // Clear
GL_AND, // And
@@ -157,7 +159,7 @@ inline GLenum LogicOp(Pica::Regs::LogicOp op) {
return logic_op_table[(unsigned)op];
}
-inline GLenum CompareFunc(Pica::Regs::CompareFunc func) {
+inline GLenum CompareFunc(Pica::FramebufferRegs::CompareFunc func) {
static const GLenum compare_func_table[] = {
GL_NEVER, // CompareFunc::Never
GL_ALWAYS, // CompareFunc::Always
@@ -180,7 +182,7 @@ inline GLenum CompareFunc(Pica::Regs::CompareFunc func) {
return compare_func_table[(unsigned)func];
}
-inline GLenum StencilOp(Pica::Regs::StencilAction action) {
+inline GLenum StencilOp(Pica::FramebufferRegs::StencilAction action) {
static const GLenum stencil_op_table[] = {
GL_KEEP, // StencilAction::Keep
GL_ZERO, // StencilAction::Zero
@@ -210,7 +212,7 @@ inline GLvec4 ColorRGBA8(const u32 color) {
}};
}
-inline std::array<GLfloat, 3> LightColor(const Pica::Regs::LightColor& color) {
+inline std::array<GLfloat, 3> LightColor(const Pica::LightingRegs::LightColor& color) {
return {{
color.r / 255.0f, color.g / 255.0f, color.b / 255.0f,
}};
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 2aa90e5c1..e19375466 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -10,8 +10,8 @@
#include "common/assert.h"
#include "common/bit_field.h"
#include "common/logging/log.h"
-#include "common/profiler_reporting.h"
-#include "common/synchronized_wrapper.h"
+#include "core/core.h"
+#include "core/core_timing.h"
#include "core/frontend/emu_window.h"
#include "core/hw/gpu.h"
#include "core/hw/hw.h"
@@ -145,21 +145,16 @@ void RendererOpenGL::SwapBuffers() {
DrawScreens();
- auto& profiler = Common::Profiling::GetProfilingManager();
- profiler.FinishFrame();
- {
- auto aggregator = Common::Profiling::GetTimingResultsAggregator();
- aggregator->AddFrame(profiler.GetPreviousFrameResults());
- }
+ Core::System::GetInstance().perf_stats.EndSystemFrame();
// Swap buffers
render_window->PollEvents();
render_window->SwapBuffers();
- prev_state.Apply();
-
- profiler.BeginFrame();
+ Core::System::GetInstance().frame_limiter.DoFrameLimiting(CoreTiming::GetGlobalTimeUs());
+ Core::System::GetInstance().perf_stats.BeginSystemFrame();
+ prev_state.Apply();
RefreshRasterizerSetting();
if (Pica::g_debug_context && Pica::g_debug_context->recorder) {
diff --git a/src/video_core/shader/shader.cpp b/src/video_core/shader/shader.cpp
index 2da50bd62..67ed19ba8 100644
--- a/src/video_core/shader/shader.cpp
+++ b/src/video_core/shader/shader.cpp
@@ -4,10 +4,12 @@
#include <cmath>
#include <cstring>
+#include "common/bit_set.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
-#include "video_core/pica.h"
#include "video_core/pica_state.h"
+#include "video_core/regs_rasterizer.h"
+#include "video_core/regs_shader.h"
#include "video_core/shader/shader.h"
#include "video_core/shader/shader_interpreter.h"
#ifdef ARCHITECTURE_x86_64
@@ -19,38 +21,31 @@ namespace Pica {
namespace Shader {
-OutputVertex OutputVertex::FromRegisters(Math::Vec4<float24> output_regs[16], const Regs& regs,
- u32 output_mask) {
+OutputVertex OutputVertex::FromAttributeBuffer(const RasterizerRegs& regs, AttributeBuffer& input) {
// Setup output data
- OutputVertex ret;
- // TODO(neobrain): Under some circumstances, up to 16 attributes may be output. We need to
- // figure out what those circumstances are and enable the remaining outputs then.
- unsigned index = 0;
- for (unsigned i = 0; i < 7; ++i) {
+ union {
+ OutputVertex ret{};
+ std::array<float24, 24> vertex_slots;
+ };
+ static_assert(sizeof(vertex_slots) == sizeof(ret), "Struct and array have different sizes.");
- if (index >= regs.vs_output_total)
- break;
+ unsigned int num_attributes = regs.vs_output_total;
+ ASSERT(num_attributes <= 7);
+ for (unsigned int i = 0; i < num_attributes; ++i) {
+ const auto& output_register_map = regs.vs_output_attributes[i];
- if ((output_mask & (1 << i)) == 0)
- continue;
-
- const auto& output_register_map = regs.vs_output_attributes[index];
-
- u32 semantics[4] = {output_register_map.map_x, output_register_map.map_y,
- output_register_map.map_z, output_register_map.map_w};
+ RasterizerRegs::VSOutputAttributes::Semantic semantics[4] = {
+ output_register_map.map_x, output_register_map.map_y, output_register_map.map_z,
+ output_register_map.map_w};
for (unsigned comp = 0; comp < 4; ++comp) {
- float24* out = ((float24*)&ret) + semantics[comp];
- if (semantics[comp] != Regs::VSOutputAttributes::INVALID) {
- *out = output_regs[i][comp];
- } else {
- // Zero output so that attributes which aren't output won't have denormals in them,
- // which would slow us down later.
- memset(out, 0, sizeof(*out));
+ RasterizerRegs::VSOutputAttributes::Semantic semantic = semantics[comp];
+ if (semantic < vertex_slots.size()) {
+ vertex_slots[semantic] = input.attr[i][comp];
+ } else if (semantic != RasterizerRegs::VSOutputAttributes::INVALID) {
+ LOG_ERROR(HW_GPU, "Invalid/unknown semantic id: %u", (unsigned int)semantic);
}
}
-
- index++;
}
// The hardware takes the absolute and saturates vertex colors like this, *before* doing
@@ -71,12 +66,20 @@ OutputVertex OutputVertex::FromRegisters(Math::Vec4<float24> output_regs[16], co
return ret;
}
-void UnitState::LoadInputVertex(const InputVertex& input, int num_attributes) {
- // Setup input register table
- const auto& attribute_register_map = g_state.regs.vs.input_register_map;
+void UnitState::LoadInput(const ShaderRegs& config, const AttributeBuffer& input) {
+ const unsigned max_attribute = config.max_input_attribute_index;
- for (int i = 0; i < num_attributes; i++)
- registers.input[attribute_register_map.GetRegisterForAttribute(i)] = input.attr[i];
+ for (unsigned attr = 0; attr <= max_attribute; ++attr) {
+ unsigned reg = config.GetRegisterForAttribute(attr);
+ registers.input[reg] = input.attr[attr];
+ }
+}
+
+void UnitState::WriteOutput(const ShaderRegs& config, AttributeBuffer& output) {
+ unsigned int output_i = 0;
+ for (unsigned int reg : Common::BitSet<u32>(config.output_mask)) {
+ output.attr[output_i++] = registers.output[reg];
+ }
}
MICROPROFILE_DEFINE(GPU_Shader, "GPU", "Shader", MP_RGB(50, 50, 240));
diff --git a/src/video_core/shader/shader.h b/src/video_core/shader/shader.h
index 44d9f76c3..38ea717ab 100644
--- a/src/video_core/shader/shader.h
+++ b/src/video_core/shader/shader.h
@@ -12,8 +12,9 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/vector_math.h"
-#include "video_core/pica.h"
#include "video_core/pica_types.h"
+#include "video_core/regs_rasterizer.h"
+#include "video_core/regs_shader.h"
using nihstro::RegisterType;
using nihstro::SourceRegister;
@@ -23,14 +24,11 @@ namespace Pica {
namespace Shader {
-struct InputVertex {
+struct AttributeBuffer {
alignas(16) Math::Vec4<float24> attr[16];
};
struct OutputVertex {
- OutputVertex() = default;
-
- // VS output attributes
Math::Vec4<float24> pos;
Math::Vec4<float24> quat;
Math::Vec4<float24> color;
@@ -42,43 +40,22 @@ struct OutputVertex {
INSERT_PADDING_WORDS(1);
Math::Vec2<float24> tc2;
- // Padding for optimal alignment
- INSERT_PADDING_WORDS(4);
-
- // Attributes used to store intermediate results
-
- // position after perspective divide
- Math::Vec3<float24> screenpos;
- INSERT_PADDING_WORDS(1);
-
- // Linear interpolation
- // factor: 0=this, 1=vtx
- void Lerp(float24 factor, const OutputVertex& vtx) {
- pos = pos * factor + vtx.pos * (float24::FromFloat32(1) - factor);
-
- // TODO: Should perform perspective correct interpolation here...
- tc0 = tc0 * factor + vtx.tc0 * (float24::FromFloat32(1) - factor);
- tc1 = tc1 * factor + vtx.tc1 * (float24::FromFloat32(1) - factor);
- tc2 = tc2 * factor + vtx.tc2 * (float24::FromFloat32(1) - factor);
-
- screenpos = screenpos * factor + vtx.screenpos * (float24::FromFloat32(1) - factor);
-
- color = color * factor + vtx.color * (float24::FromFloat32(1) - factor);
- }
-
- // Linear interpolation
- // factor: 0=v0, 1=v1
- static OutputVertex Lerp(float24 factor, const OutputVertex& v0, const OutputVertex& v1) {
- OutputVertex ret = v0;
- ret.Lerp(factor, v1);
- return ret;
- }
-
- static OutputVertex FromRegisters(Math::Vec4<float24> output_regs[16], const Regs& regs,
- u32 output_mask);
+ static OutputVertex FromAttributeBuffer(const RasterizerRegs& regs, AttributeBuffer& output);
};
+#define ASSERT_POS(var, pos) \
+ static_assert(offsetof(OutputVertex, var) == pos * sizeof(float24), "Semantic at wrong " \
+ "offset.")
+ASSERT_POS(pos, RasterizerRegs::VSOutputAttributes::POSITION_X);
+ASSERT_POS(quat, RasterizerRegs::VSOutputAttributes::QUATERNION_X);
+ASSERT_POS(color, RasterizerRegs::VSOutputAttributes::COLOR_R);
+ASSERT_POS(tc0, RasterizerRegs::VSOutputAttributes::TEXCOORD0_U);
+ASSERT_POS(tc1, RasterizerRegs::VSOutputAttributes::TEXCOORD1_U);
+ASSERT_POS(tc0_w, RasterizerRegs::VSOutputAttributes::TEXCOORD0_W);
+ASSERT_POS(view, RasterizerRegs::VSOutputAttributes::VIEW_X);
+ASSERT_POS(tc2, RasterizerRegs::VSOutputAttributes::TEXCOORD2_U);
+#undef ASSERT_POS
static_assert(std::is_pod<OutputVertex>::value, "Structure is not POD");
-static_assert(sizeof(OutputVertex) == 32 * sizeof(float), "OutputVertex has invalid size");
+static_assert(sizeof(OutputVertex) == 24 * sizeof(float), "OutputVertex has invalid size");
/**
* This structure contains the state information that needs to be unique for a shader unit. The 3DS
@@ -137,10 +114,12 @@ struct UnitState {
/**
* Loads the unit state with an input vertex.
*
- * @param input Input vertex into the shader
- * @param num_attributes The number of vertex shader attributes to load
+ * @param config Shader configuration registers corresponding to the unit.
+ * @param input Attribute buffer to load into the input registers.
*/
- void LoadInputVertex(const InputVertex& input, int num_attributes);
+ void LoadInput(const ShaderRegs& config, const AttributeBuffer& input);
+
+ void WriteOutput(const ShaderRegs& config, AttributeBuffer& output);
};
struct ShaderSetup {
diff --git a/src/video_core/shader/shader_interpreter.cpp b/src/video_core/shader/shader_interpreter.cpp
index c0c89b857..f4d1c46c5 100644
--- a/src/video_core/shader/shader_interpreter.cpp
+++ b/src/video_core/shader/shader_interpreter.cpp
@@ -668,14 +668,14 @@ void InterpreterEngine::Run(const ShaderSetup& setup, UnitState& state) const {
}
DebugData<true> InterpreterEngine::ProduceDebugInfo(const ShaderSetup& setup,
- const InputVertex& input,
- int num_attributes) const {
+ const AttributeBuffer& input,
+ const ShaderRegs& config) const {
UnitState state;
DebugData<true> debug_data;
// Setup input register table
boost::fill(state.registers.input, Math::Vec4<float24>::AssignToAll(float24::Zero()));
- state.LoadInputVertex(input, num_attributes);
+ state.LoadInput(config, input);
RunInterpreter(setup, state, debug_data, setup.engine_data.entry_point);
return debug_data;
}
diff --git a/src/video_core/shader/shader_interpreter.h b/src/video_core/shader/shader_interpreter.h
index d6c0e2d8c..50fd7c69d 100644
--- a/src/video_core/shader/shader_interpreter.h
+++ b/src/video_core/shader/shader_interpreter.h
@@ -18,13 +18,13 @@ public:
/**
* Produce debug information based on the given shader and input vertex
- * @param input Input vertex into the shader
- * @param num_attributes The number of vertex shader attributes
+ * @param setup Shader engine state
+ * @param input Input vertex into the shader
* @param config Configuration object for the shader pipeline
* @return Debug information for this shader with regards to the given vertex
*/
- DebugData<true> ProduceDebugInfo(const ShaderSetup& setup, const InputVertex& input,
- int num_attributes) const;
+ DebugData<true> ProduceDebugInfo(const ShaderSetup& setup, const AttributeBuffer& input,
+ const ShaderRegs& config) const;
};
} // namespace
diff --git a/src/video_core/shader/shader_jit_x64_compiler.cpp b/src/video_core/shader/shader_jit_x64_compiler.cpp
index 49806e8c9..2dbc8b147 100644
--- a/src/video_core/shader/shader_jit_x64_compiler.cpp
+++ b/src/video_core/shader/shader_jit_x64_compiler.cpp
@@ -144,6 +144,8 @@ static const BitSet32 persistent_regs = BuildRegSet({
ADDROFFS_REG_0, ADDROFFS_REG_1, LOOPCOUNT_REG, COND0, COND1,
// Constants
ONE, NEGBIT,
+ // Loop variables
+ LOOPCOUNT, LOOPINC,
});
/// Raw constant for the source register selector that indicates no swizzling is performed
@@ -293,14 +295,22 @@ void JitShader::Compile_DestEnable(Instruction instr, Xmm src) {
}
void JitShader::Compile_SanitizedMul(Xmm src1, Xmm src2, Xmm scratch) {
+ // 0 * inf and inf * 0 in the PICA should return 0 instead of NaN. This can be implemented by
+ // checking for NaNs before and after the multiplication. If the multiplication result is NaN
+ // where neither source was, this NaN was generated by a 0 * inf multiplication, and so the
+ // result should be transformed to 0 to match PICA fp rules.
+
+ // Set scratch to mask of (src1 != NaN and src2 != NaN)
movaps(scratch, src1);
cmpordps(scratch, src2);
mulps(src1, src2);
+ // Set src2 to mask of (result == NaN)
movaps(src2, src1);
cmpunordps(src2, src2);
+ // Clear components where scratch != src2 (i.e. if result is NaN where neither source was NaN)
xorps(scratch, src2);
andps(src1, scratch);
}
@@ -587,7 +597,7 @@ void JitShader::Compile_RSQ(Instruction instr) {
void JitShader::Compile_NOP(Instruction instr) {}
void JitShader::Compile_END(Instruction instr) {
- ABI_PopRegistersAndAdjustStack(*this, ABI_ALL_CALLEE_SAVED, 8);
+ ABI_PopRegistersAndAdjustStack(*this, ABI_ALL_CALLEE_SAVED, 8, 16);
ret();
}
@@ -839,7 +849,10 @@ void JitShader::Compile(const std::array<u32, 1024>* program_code_,
FindReturnOffsets();
// The stack pointer is 8 modulo 16 at the entry of a procedure
- ABI_PushRegistersAndAdjustStack(*this, ABI_ALL_CALLEE_SAVED, 8);
+ // We reserve 16 bytes and assign a dummy value to the first 8 bytes, to catch any potential
+ // return checks (see Compile_Return) that happen in shader main routine.
+ ABI_PushRegistersAndAdjustStack(*this, ABI_ALL_CALLEE_SAVED, 8, 16);
+ mov(qword[rsp + 8], 0xFFFFFFFFFFFFFFFFULL);
mov(SETUP, ABI_PARAM1);
mov(STATE, ABI_PARAM2);
diff --git a/src/video_core/shader/shader_jit_x64_compiler.h b/src/video_core/shader/shader_jit_x64_compiler.h
index 29e9875ea..f27675560 100644
--- a/src/video_core/shader/shader_jit_x64_compiler.h
+++ b/src/video_core/shader/shader_jit_x64_compiler.h
@@ -12,7 +12,6 @@
#include <xbyak.h>
#include "common/bit_set.h"
#include "common/common_types.h"
-#include "common/x64/emitter.h"
#include "video_core/shader/shader.h"
using nihstro::Instruction;
@@ -94,7 +93,8 @@ private:
/**
* Assertion evaluated at compile-time, but only triggered if executed at runtime.
- * @param msg Message to be logged if the assertion fails.
+ * @param condition Condition to be evaluated.
+ * @param msg Message to be logged if the assertion fails.
*/
void Compile_Assert(bool condition, const char* msg);
diff --git a/src/video_core/clipper.cpp b/src/video_core/swrasterizer/clipper.cpp
index 05b5cea73..2d80822d9 100644
--- a/src/video_core/clipper.cpp
+++ b/src/video_core/swrasterizer/clipper.cpp
@@ -11,12 +11,13 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/vector_math.h"
-#include "video_core/clipper.h"
-#include "video_core/pica.h"
#include "video_core/pica_state.h"
#include "video_core/pica_types.h"
-#include "video_core/rasterizer.h"
#include "video_core/shader/shader.h"
+#include "video_core/swrasterizer/clipper.h"
+#include "video_core/swrasterizer/rasterizer.h"
+
+using Pica::Rasterizer::Vertex;
namespace Pica {
@@ -29,20 +30,20 @@ public:
float24::FromFloat32(0), float24::FromFloat32(0)))
: coeffs(coeffs), bias(bias) {}
- bool IsInside(const OutputVertex& vertex) const {
+ bool IsInside(const Vertex& vertex) const {
return Math::Dot(vertex.pos + bias, coeffs) <= float24::FromFloat32(0);
}
- bool IsOutSide(const OutputVertex& vertex) const {
+ bool IsOutSide(const Vertex& vertex) const {
return !IsInside(vertex);
}
- OutputVertex GetIntersection(const OutputVertex& v0, const OutputVertex& v1) const {
+ Vertex GetIntersection(const Vertex& v0, const Vertex& v1) const {
float24 dp = Math::Dot(v0.pos + bias, coeffs);
float24 dp_prev = Math::Dot(v1.pos + bias, coeffs);
float24 factor = dp_prev / (dp_prev - dp);
- return OutputVertex::Lerp(factor, v0, v1);
+ return Vertex::Lerp(factor, v0, v1);
}
private:
@@ -51,7 +52,7 @@ private:
Math::Vec4<float24> bias;
};
-static void InitScreenCoordinates(OutputVertex& vtx) {
+static void InitScreenCoordinates(Vertex& vtx) {
struct {
float24 halfsize_x;
float24 offset_x;
@@ -62,10 +63,10 @@ static void InitScreenCoordinates(OutputVertex& vtx) {
} viewport;
const auto& regs = g_state.regs;
- viewport.halfsize_x = float24::FromRaw(regs.viewport_size_x);
- viewport.halfsize_y = float24::FromRaw(regs.viewport_size_y);
- viewport.offset_x = float24::FromFloat32(static_cast<float>(regs.viewport_corner.x));
- viewport.offset_y = float24::FromFloat32(static_cast<float>(regs.viewport_corner.y));
+ viewport.halfsize_x = float24::FromRaw(regs.rasterizer.viewport_size_x);
+ viewport.halfsize_y = float24::FromRaw(regs.rasterizer.viewport_size_y);
+ viewport.offset_x = float24::FromFloat32(static_cast<float>(regs.rasterizer.viewport_corner.x));
+ viewport.offset_y = float24::FromFloat32(static_cast<float>(regs.rasterizer.viewport_corner.y));
float24 inv_w = float24::FromFloat32(1.f) / vtx.pos.w;
vtx.color *= inv_w;
@@ -91,8 +92,8 @@ void ProcessTriangle(const OutputVertex& v0, const OutputVertex& v1, const Outpu
// introduces at most 1 new vertex to the polygon. Since we start with a triangle and have a
// fixed 6 clipping planes, the maximum number of vertices of the clipped polygon is 3 + 6 = 9.
static const size_t MAX_VERTICES = 9;
- static_vector<OutputVertex, MAX_VERTICES> buffer_a = {v0, v1, v2};
- static_vector<OutputVertex, MAX_VERTICES> buffer_b;
+ static_vector<Vertex, MAX_VERTICES> buffer_a = {v0, v1, v2};
+ static_vector<Vertex, MAX_VERTICES> buffer_b;
auto* output_list = &buffer_a;
auto* input_list = &buffer_b;
@@ -123,7 +124,7 @@ void ProcessTriangle(const OutputVertex& v0, const OutputVertex& v1, const Outpu
std::swap(input_list, output_list);
output_list->clear();
- const OutputVertex* reference_vertex = &input_list->back();
+ const Vertex* reference_vertex = &input_list->back();
for (const auto& vertex : *input_list) {
// NOTE: This algorithm changes vertex order in some cases!
@@ -148,9 +149,9 @@ void ProcessTriangle(const OutputVertex& v0, const OutputVertex& v1, const Outpu
InitScreenCoordinates((*output_list)[1]);
for (size_t i = 0; i < output_list->size() - 2; i++) {
- OutputVertex& vtx0 = (*output_list)[0];
- OutputVertex& vtx1 = (*output_list)[i + 1];
- OutputVertex& vtx2 = (*output_list)[i + 2];
+ Vertex& vtx0 = (*output_list)[0];
+ Vertex& vtx1 = (*output_list)[i + 1];
+ Vertex& vtx2 = (*output_list)[i + 2];
InitScreenCoordinates(vtx2);
diff --git a/src/video_core/clipper.h b/src/video_core/swrasterizer/clipper.h
index b51af0af9..b51af0af9 100644
--- a/src/video_core/clipper.h
+++ b/src/video_core/swrasterizer/clipper.h
diff --git a/src/video_core/swrasterizer/framebuffer.cpp b/src/video_core/swrasterizer/framebuffer.cpp
new file mode 100644
index 000000000..7de3aac75
--- /dev/null
+++ b/src/video_core/swrasterizer/framebuffer.cpp
@@ -0,0 +1,358 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+
+#include "common/assert.h"
+#include "common/color.h"
+#include "common/common_types.h"
+#include "common/logging/log.h"
+#include "common/math_util.h"
+#include "common/vector_math.h"
+#include "core/hw/gpu.h"
+#include "core/memory.h"
+#include "video_core/pica_state.h"
+#include "video_core/regs_framebuffer.h"
+#include "video_core/swrasterizer/framebuffer.h"
+#include "video_core/utils.h"
+
+namespace Pica {
+namespace Rasterizer {
+
+void DrawPixel(int x, int y, const Math::Vec4<u8>& color) {
+ const auto& framebuffer = g_state.regs.framebuffer.framebuffer;
+ const PAddr addr = framebuffer.GetColorBufferPhysicalAddress();
+
+ // Similarly to textures, the render framebuffer is laid out from bottom to top, too.
+ // NOTE: The framebuffer height register contains the actual FB height minus one.
+ y = framebuffer.height - y;
+
+ const u32 coarse_y = y & ~7;
+ u32 bytes_per_pixel =
+ GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(framebuffer.color_format.Value()));
+ u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) +
+ coarse_y * framebuffer.width * bytes_per_pixel;
+ u8* dst_pixel = Memory::GetPhysicalPointer(addr) + dst_offset;
+
+ switch (framebuffer.color_format) {
+ case FramebufferRegs::ColorFormat::RGBA8:
+ Color::EncodeRGBA8(color, dst_pixel);
+ break;
+
+ case FramebufferRegs::ColorFormat::RGB8:
+ Color::EncodeRGB8(color, dst_pixel);
+ break;
+
+ case FramebufferRegs::ColorFormat::RGB5A1:
+ Color::EncodeRGB5A1(color, dst_pixel);
+ break;
+
+ case FramebufferRegs::ColorFormat::RGB565:
+ Color::EncodeRGB565(color, dst_pixel);
+ break;
+
+ case FramebufferRegs::ColorFormat::RGBA4:
+ Color::EncodeRGBA4(color, dst_pixel);
+ break;
+
+ default:
+ LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x",
+ framebuffer.color_format.Value());
+ UNIMPLEMENTED();
+ }
+}
+
+const Math::Vec4<u8> GetPixel(int x, int y) {
+ const auto& framebuffer = g_state.regs.framebuffer.framebuffer;
+ const PAddr addr = framebuffer.GetColorBufferPhysicalAddress();
+
+ y = framebuffer.height - y;
+
+ const u32 coarse_y = y & ~7;
+ u32 bytes_per_pixel =
+ GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(framebuffer.color_format.Value()));
+ u32 src_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) +
+ coarse_y * framebuffer.width * bytes_per_pixel;
+ u8* src_pixel = Memory::GetPhysicalPointer(addr) + src_offset;
+
+ switch (framebuffer.color_format) {
+ case FramebufferRegs::ColorFormat::RGBA8:
+ return Color::DecodeRGBA8(src_pixel);
+
+ case FramebufferRegs::ColorFormat::RGB8:
+ return Color::DecodeRGB8(src_pixel);
+
+ case FramebufferRegs::ColorFormat::RGB5A1:
+ return Color::DecodeRGB5A1(src_pixel);
+
+ case FramebufferRegs::ColorFormat::RGB565:
+ return Color::DecodeRGB565(src_pixel);
+
+ case FramebufferRegs::ColorFormat::RGBA4:
+ return Color::DecodeRGBA4(src_pixel);
+
+ default:
+ LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x",
+ framebuffer.color_format.Value());
+ UNIMPLEMENTED();
+ }
+
+ return {0, 0, 0, 0};
+}
+
+u32 GetDepth(int x, int y) {
+ const auto& framebuffer = g_state.regs.framebuffer.framebuffer;
+ const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress();
+ u8* depth_buffer = Memory::GetPhysicalPointer(addr);
+
+ y = framebuffer.height - y;
+
+ const u32 coarse_y = y & ~7;
+ u32 bytes_per_pixel = FramebufferRegs::BytesPerDepthPixel(framebuffer.depth_format);
+ u32 stride = framebuffer.width * bytes_per_pixel;
+
+ u32 src_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
+ u8* src_pixel = depth_buffer + src_offset;
+
+ switch (framebuffer.depth_format) {
+ case FramebufferRegs::DepthFormat::D16:
+ return Color::DecodeD16(src_pixel);
+ case FramebufferRegs::DepthFormat::D24:
+ return Color::DecodeD24(src_pixel);
+ case FramebufferRegs::DepthFormat::D24S8:
+ return Color::DecodeD24S8(src_pixel).x;
+ default:
+ LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format);
+ UNIMPLEMENTED();
+ return 0;
+ }
+}
+
+u8 GetStencil(int x, int y) {
+ const auto& framebuffer = g_state.regs.framebuffer.framebuffer;
+ const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress();
+ u8* depth_buffer = Memory::GetPhysicalPointer(addr);
+
+ y = framebuffer.height - y;
+
+ const u32 coarse_y = y & ~7;
+ u32 bytes_per_pixel = Pica::FramebufferRegs::BytesPerDepthPixel(framebuffer.depth_format);
+ u32 stride = framebuffer.width * bytes_per_pixel;
+
+ u32 src_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
+ u8* src_pixel = depth_buffer + src_offset;
+
+ switch (framebuffer.depth_format) {
+ case FramebufferRegs::DepthFormat::D24S8:
+ return Color::DecodeD24S8(src_pixel).y;
+
+ default:
+ LOG_WARNING(
+ HW_GPU,
+ "GetStencil called for function which doesn't have a stencil component (format %u)",
+ framebuffer.depth_format);
+ return 0;
+ }
+}
+
+void SetDepth(int x, int y, u32 value) {
+ const auto& framebuffer = g_state.regs.framebuffer.framebuffer;
+ const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress();
+ u8* depth_buffer = Memory::GetPhysicalPointer(addr);
+
+ y = framebuffer.height - y;
+
+ const u32 coarse_y = y & ~7;
+ u32 bytes_per_pixel = FramebufferRegs::BytesPerDepthPixel(framebuffer.depth_format);
+ u32 stride = framebuffer.width * bytes_per_pixel;
+
+ u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
+ u8* dst_pixel = depth_buffer + dst_offset;
+
+ switch (framebuffer.depth_format) {
+ case FramebufferRegs::DepthFormat::D16:
+ Color::EncodeD16(value, dst_pixel);
+ break;
+
+ case FramebufferRegs::DepthFormat::D24:
+ Color::EncodeD24(value, dst_pixel);
+ break;
+
+ case FramebufferRegs::DepthFormat::D24S8:
+ Color::EncodeD24X8(value, dst_pixel);
+ break;
+
+ default:
+ LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format);
+ UNIMPLEMENTED();
+ break;
+ }
+}
+
+void SetStencil(int x, int y, u8 value) {
+ const auto& framebuffer = g_state.regs.framebuffer.framebuffer;
+ const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress();
+ u8* depth_buffer = Memory::GetPhysicalPointer(addr);
+
+ y = framebuffer.height - y;
+
+ const u32 coarse_y = y & ~7;
+ u32 bytes_per_pixel = Pica::FramebufferRegs::BytesPerDepthPixel(framebuffer.depth_format);
+ u32 stride = framebuffer.width * bytes_per_pixel;
+
+ u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
+ u8* dst_pixel = depth_buffer + dst_offset;
+
+ switch (framebuffer.depth_format) {
+ case Pica::FramebufferRegs::DepthFormat::D16:
+ case Pica::FramebufferRegs::DepthFormat::D24:
+ // Nothing to do
+ break;
+
+ case Pica::FramebufferRegs::DepthFormat::D24S8:
+ Color::EncodeX24S8(value, dst_pixel);
+ break;
+
+ default:
+ LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format);
+ UNIMPLEMENTED();
+ break;
+ }
+}
+
+u8 PerformStencilAction(FramebufferRegs::StencilAction action, u8 old_stencil, u8 ref) {
+ switch (action) {
+ case FramebufferRegs::StencilAction::Keep:
+ return old_stencil;
+
+ case FramebufferRegs::StencilAction::Zero:
+ return 0;
+
+ case FramebufferRegs::StencilAction::Replace:
+ return ref;
+
+ case FramebufferRegs::StencilAction::Increment:
+ // Saturated increment
+ return std::min<u8>(old_stencil, 254) + 1;
+
+ case FramebufferRegs::StencilAction::Decrement:
+ // Saturated decrement
+ return std::max<u8>(old_stencil, 1) - 1;
+
+ case FramebufferRegs::StencilAction::Invert:
+ return ~old_stencil;
+
+ case FramebufferRegs::StencilAction::IncrementWrap:
+ return old_stencil + 1;
+
+ case FramebufferRegs::StencilAction::DecrementWrap:
+ return old_stencil - 1;
+
+ default:
+ LOG_CRITICAL(HW_GPU, "Unknown stencil action %x", (int)action);
+ UNIMPLEMENTED();
+ return 0;
+ }
+}
+
+Math::Vec4<u8> EvaluateBlendEquation(const Math::Vec4<u8>& src, const Math::Vec4<u8>& srcfactor,
+ const Math::Vec4<u8>& dest, const Math::Vec4<u8>& destfactor,
+ FramebufferRegs::BlendEquation equation) {
+ Math::Vec4<int> result;
+
+ auto src_result = (src * srcfactor).Cast<int>();
+ auto dst_result = (dest * destfactor).Cast<int>();
+
+ switch (equation) {
+ case FramebufferRegs::BlendEquation::Add:
+ result = (src_result + dst_result) / 255;
+ break;
+
+ case FramebufferRegs::BlendEquation::Subtract:
+ result = (src_result - dst_result) / 255;
+ break;
+
+ case FramebufferRegs::BlendEquation::ReverseSubtract:
+ result = (dst_result - src_result) / 255;
+ break;
+
+ // TODO: How do these two actually work? OpenGL doesn't include the blend factors in the
+ // min/max computations, but is this what the 3DS actually does?
+ case FramebufferRegs::BlendEquation::Min:
+ result.r() = std::min(src.r(), dest.r());
+ result.g() = std::min(src.g(), dest.g());
+ result.b() = std::min(src.b(), dest.b());
+ result.a() = std::min(src.a(), dest.a());
+ break;
+
+ case FramebufferRegs::BlendEquation::Max:
+ result.r() = std::max(src.r(), dest.r());
+ result.g() = std::max(src.g(), dest.g());
+ result.b() = std::max(src.b(), dest.b());
+ result.a() = std::max(src.a(), dest.a());
+ break;
+
+ default:
+ LOG_CRITICAL(HW_GPU, "Unknown RGB blend equation %x", equation);
+ UNIMPLEMENTED();
+ }
+
+ return Math::Vec4<u8>(MathUtil::Clamp(result.r(), 0, 255), MathUtil::Clamp(result.g(), 0, 255),
+ MathUtil::Clamp(result.b(), 0, 255), MathUtil::Clamp(result.a(), 0, 255));
+};
+
+u8 LogicOp(u8 src, u8 dest, FramebufferRegs::LogicOp op) {
+ switch (op) {
+ case FramebufferRegs::LogicOp::Clear:
+ return 0;
+
+ case FramebufferRegs::LogicOp::And:
+ return src & dest;
+
+ case FramebufferRegs::LogicOp::AndReverse:
+ return src & ~dest;
+
+ case FramebufferRegs::LogicOp::Copy:
+ return src;
+
+ case FramebufferRegs::LogicOp::Set:
+ return 255;
+
+ case FramebufferRegs::LogicOp::CopyInverted:
+ return ~src;
+
+ case FramebufferRegs::LogicOp::NoOp:
+ return dest;
+
+ case FramebufferRegs::LogicOp::Invert:
+ return ~dest;
+
+ case FramebufferRegs::LogicOp::Nand:
+ return ~(src & dest);
+
+ case FramebufferRegs::LogicOp::Or:
+ return src | dest;
+
+ case FramebufferRegs::LogicOp::Nor:
+ return ~(src | dest);
+
+ case FramebufferRegs::LogicOp::Xor:
+ return src ^ dest;
+
+ case FramebufferRegs::LogicOp::Equiv:
+ return ~(src ^ dest);
+
+ case FramebufferRegs::LogicOp::AndInverted:
+ return ~src & dest;
+
+ case FramebufferRegs::LogicOp::OrReverse:
+ return src | ~dest;
+
+ case FramebufferRegs::LogicOp::OrInverted:
+ return ~src | dest;
+ }
+};
+
+} // namespace Rasterizer
+} // namespace Pica
diff --git a/src/video_core/swrasterizer/framebuffer.h b/src/video_core/swrasterizer/framebuffer.h
new file mode 100644
index 000000000..4a32a4979
--- /dev/null
+++ b/src/video_core/swrasterizer/framebuffer.h
@@ -0,0 +1,29 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "common/vector_math.h"
+#include "video_core/regs_framebuffer.h"
+
+namespace Pica {
+namespace Rasterizer {
+
+void DrawPixel(int x, int y, const Math::Vec4<u8>& color);
+const Math::Vec4<u8> GetPixel(int x, int y);
+u32 GetDepth(int x, int y);
+u8 GetStencil(int x, int y);
+void SetDepth(int x, int y, u32 value);
+void SetStencil(int x, int y, u8 value);
+u8 PerformStencilAction(FramebufferRegs::StencilAction action, u8 old_stencil, u8 ref);
+
+Math::Vec4<u8> EvaluateBlendEquation(const Math::Vec4<u8>& src, const Math::Vec4<u8>& srcfactor,
+ const Math::Vec4<u8>& dest, const Math::Vec4<u8>& destfactor,
+ FramebufferRegs::BlendEquation equation);
+
+u8 LogicOp(u8 src, u8 dest, FramebufferRegs::LogicOp op);
+
+} // namespace Rasterizer
+} // namespace Pica
diff --git a/src/video_core/rasterizer.cpp b/src/video_core/swrasterizer/rasterizer.cpp
index b9f5d4533..7557fcb89 100644
--- a/src/video_core/rasterizer.cpp
+++ b/src/video_core/swrasterizer/rasterizer.cpp
@@ -16,253 +16,21 @@
#include "core/hw/gpu.h"
#include "core/memory.h"
#include "video_core/debug_utils/debug_utils.h"
-#include "video_core/pica.h"
#include "video_core/pica_state.h"
#include "video_core/pica_types.h"
-#include "video_core/rasterizer.h"
+#include "video_core/regs_framebuffer.h"
+#include "video_core/regs_rasterizer.h"
+#include "video_core/regs_texturing.h"
#include "video_core/shader/shader.h"
+#include "video_core/swrasterizer/framebuffer.h"
+#include "video_core/swrasterizer/rasterizer.h"
+#include "video_core/swrasterizer/texturing.h"
+#include "video_core/texture/texture_decode.h"
#include "video_core/utils.h"
namespace Pica {
-
namespace Rasterizer {
-static void DrawPixel(int x, int y, const Math::Vec4<u8>& color) {
- const auto& framebuffer = g_state.regs.framebuffer;
- const PAddr addr = framebuffer.GetColorBufferPhysicalAddress();
-
- // Similarly to textures, the render framebuffer is laid out from bottom to top, too.
- // NOTE: The framebuffer height register contains the actual FB height minus one.
- y = framebuffer.height - y;
-
- const u32 coarse_y = y & ~7;
- u32 bytes_per_pixel =
- GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(framebuffer.color_format.Value()));
- u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) +
- coarse_y * framebuffer.width * bytes_per_pixel;
- u8* dst_pixel = Memory::GetPhysicalPointer(addr) + dst_offset;
-
- switch (framebuffer.color_format) {
- case Regs::ColorFormat::RGBA8:
- Color::EncodeRGBA8(color, dst_pixel);
- break;
-
- case Regs::ColorFormat::RGB8:
- Color::EncodeRGB8(color, dst_pixel);
- break;
-
- case Regs::ColorFormat::RGB5A1:
- Color::EncodeRGB5A1(color, dst_pixel);
- break;
-
- case Regs::ColorFormat::RGB565:
- Color::EncodeRGB565(color, dst_pixel);
- break;
-
- case Regs::ColorFormat::RGBA4:
- Color::EncodeRGBA4(color, dst_pixel);
- break;
-
- default:
- LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x",
- framebuffer.color_format.Value());
- UNIMPLEMENTED();
- }
-}
-
-static const Math::Vec4<u8> GetPixel(int x, int y) {
- const auto& framebuffer = g_state.regs.framebuffer;
- const PAddr addr = framebuffer.GetColorBufferPhysicalAddress();
-
- y = framebuffer.height - y;
-
- const u32 coarse_y = y & ~7;
- u32 bytes_per_pixel =
- GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(framebuffer.color_format.Value()));
- u32 src_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) +
- coarse_y * framebuffer.width * bytes_per_pixel;
- u8* src_pixel = Memory::GetPhysicalPointer(addr) + src_offset;
-
- switch (framebuffer.color_format) {
- case Regs::ColorFormat::RGBA8:
- return Color::DecodeRGBA8(src_pixel);
-
- case Regs::ColorFormat::RGB8:
- return Color::DecodeRGB8(src_pixel);
-
- case Regs::ColorFormat::RGB5A1:
- return Color::DecodeRGB5A1(src_pixel);
-
- case Regs::ColorFormat::RGB565:
- return Color::DecodeRGB565(src_pixel);
-
- case Regs::ColorFormat::RGBA4:
- return Color::DecodeRGBA4(src_pixel);
-
- default:
- LOG_CRITICAL(Render_Software, "Unknown framebuffer color format %x",
- framebuffer.color_format.Value());
- UNIMPLEMENTED();
- }
-
- return {0, 0, 0, 0};
-}
-
-static u32 GetDepth(int x, int y) {
- const auto& framebuffer = g_state.regs.framebuffer;
- const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress();
- u8* depth_buffer = Memory::GetPhysicalPointer(addr);
-
- y = framebuffer.height - y;
-
- const u32 coarse_y = y & ~7;
- u32 bytes_per_pixel = Regs::BytesPerDepthPixel(framebuffer.depth_format);
- u32 stride = framebuffer.width * bytes_per_pixel;
-
- u32 src_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
- u8* src_pixel = depth_buffer + src_offset;
-
- switch (framebuffer.depth_format) {
- case Regs::DepthFormat::D16:
- return Color::DecodeD16(src_pixel);
- case Regs::DepthFormat::D24:
- return Color::DecodeD24(src_pixel);
- case Regs::DepthFormat::D24S8:
- return Color::DecodeD24S8(src_pixel).x;
- default:
- LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format);
- UNIMPLEMENTED();
- return 0;
- }
-}
-
-static u8 GetStencil(int x, int y) {
- const auto& framebuffer = g_state.regs.framebuffer;
- const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress();
- u8* depth_buffer = Memory::GetPhysicalPointer(addr);
-
- y = framebuffer.height - y;
-
- const u32 coarse_y = y & ~7;
- u32 bytes_per_pixel = Pica::Regs::BytesPerDepthPixel(framebuffer.depth_format);
- u32 stride = framebuffer.width * bytes_per_pixel;
-
- u32 src_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
- u8* src_pixel = depth_buffer + src_offset;
-
- switch (framebuffer.depth_format) {
- case Regs::DepthFormat::D24S8:
- return Color::DecodeD24S8(src_pixel).y;
-
- default:
- LOG_WARNING(
- HW_GPU,
- "GetStencil called for function which doesn't have a stencil component (format %u)",
- framebuffer.depth_format);
- return 0;
- }
-}
-
-static void SetDepth(int x, int y, u32 value) {
- const auto& framebuffer = g_state.regs.framebuffer;
- const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress();
- u8* depth_buffer = Memory::GetPhysicalPointer(addr);
-
- y = framebuffer.height - y;
-
- const u32 coarse_y = y & ~7;
- u32 bytes_per_pixel = Regs::BytesPerDepthPixel(framebuffer.depth_format);
- u32 stride = framebuffer.width * bytes_per_pixel;
-
- u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
- u8* dst_pixel = depth_buffer + dst_offset;
-
- switch (framebuffer.depth_format) {
- case Regs::DepthFormat::D16:
- Color::EncodeD16(value, dst_pixel);
- break;
-
- case Regs::DepthFormat::D24:
- Color::EncodeD24(value, dst_pixel);
- break;
-
- case Regs::DepthFormat::D24S8:
- Color::EncodeD24X8(value, dst_pixel);
- break;
-
- default:
- LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format);
- UNIMPLEMENTED();
- break;
- }
-}
-
-static void SetStencil(int x, int y, u8 value) {
- const auto& framebuffer = g_state.regs.framebuffer;
- const PAddr addr = framebuffer.GetDepthBufferPhysicalAddress();
- u8* depth_buffer = Memory::GetPhysicalPointer(addr);
-
- y = framebuffer.height - y;
-
- const u32 coarse_y = y & ~7;
- u32 bytes_per_pixel = Pica::Regs::BytesPerDepthPixel(framebuffer.depth_format);
- u32 stride = framebuffer.width * bytes_per_pixel;
-
- u32 dst_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + coarse_y * stride;
- u8* dst_pixel = depth_buffer + dst_offset;
-
- switch (framebuffer.depth_format) {
- case Pica::Regs::DepthFormat::D16:
- case Pica::Regs::DepthFormat::D24:
- // Nothing to do
- break;
-
- case Pica::Regs::DepthFormat::D24S8:
- Color::EncodeX24S8(value, dst_pixel);
- break;
-
- default:
- LOG_CRITICAL(HW_GPU, "Unimplemented depth format %u", framebuffer.depth_format);
- UNIMPLEMENTED();
- break;
- }
-}
-
-static u8 PerformStencilAction(Regs::StencilAction action, u8 old_stencil, u8 ref) {
- switch (action) {
- case Regs::StencilAction::Keep:
- return old_stencil;
-
- case Regs::StencilAction::Zero:
- return 0;
-
- case Regs::StencilAction::Replace:
- return ref;
-
- case Regs::StencilAction::Increment:
- // Saturated increment
- return std::min<u8>(old_stencil, 254) + 1;
-
- case Regs::StencilAction::Decrement:
- // Saturated decrement
- return std::max<u8>(old_stencil, 1) - 1;
-
- case Regs::StencilAction::Invert:
- return ~old_stencil;
-
- case Regs::StencilAction::IncrementWrap:
- return old_stencil + 1;
-
- case Regs::StencilAction::DecrementWrap:
- return old_stencil - 1;
-
- default:
- LOG_CRITICAL(HW_GPU, "Unknown stencil action %x", (int)action);
- UNIMPLEMENTED();
- return 0;
- }
-}
-
// NOTE: Assuming that rasterizer coordinates are 12.4 fixed-point values
struct Fix12P4 {
Fix12P4() {}
@@ -307,8 +75,8 @@ MICROPROFILE_DEFINE(GPU_Rasterization, "GPU", "Rasterization", MP_RGB(50, 50, 24
* Helper function for ProcessTriangle with the "reversed" flag to allow for implementing
* culling via recursion.
*/
-static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader::OutputVertex& v1,
- const Shader::OutputVertex& v2, bool reversed = false) {
+static void ProcessTriangleInternal(const Vertex& v0, const Vertex& v1, const Vertex& v2,
+ bool reversed = false) {
const auto& regs = g_state.regs;
MICROPROFILE_SCOPE(GPU_Rasterization);
@@ -326,14 +94,14 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader
ScreenToRasterizerCoordinates(v1.screenpos),
ScreenToRasterizerCoordinates(v2.screenpos)};
- if (regs.cull_mode == Regs::CullMode::KeepAll) {
+ if (regs.rasterizer.cull_mode == RasterizerRegs::CullMode::KeepAll) {
// Make sure we always end up with a triangle wound counter-clockwise
if (!reversed && SignedArea(vtxpos[0].xy(), vtxpos[1].xy(), vtxpos[2].xy()) <= 0) {
ProcessTriangleInternal(v0, v2, v1, true);
return;
}
} else {
- if (!reversed && regs.cull_mode == Regs::CullMode::KeepClockWise) {
+ if (!reversed && regs.rasterizer.cull_mode == RasterizerRegs::CullMode::KeepClockWise) {
// Reverse vertex order and use the CCW code path.
ProcessTriangleInternal(v0, v2, v1, true);
return;
@@ -350,13 +118,13 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader
u16 max_y = std::max({vtxpos[0].y, vtxpos[1].y, vtxpos[2].y});
// Convert the scissor box coordinates to 12.4 fixed point
- u16 scissor_x1 = (u16)(regs.scissor_test.x1 << 4);
- u16 scissor_y1 = (u16)(regs.scissor_test.y1 << 4);
+ u16 scissor_x1 = (u16)(regs.rasterizer.scissor_test.x1 << 4);
+ u16 scissor_y1 = (u16)(regs.rasterizer.scissor_test.y1 << 4);
// x2,y2 have +1 added to cover the entire sub-pixel area
- u16 scissor_x2 = (u16)((regs.scissor_test.x2 + 1) << 4);
- u16 scissor_y2 = (u16)((regs.scissor_test.y2 + 1) << 4);
+ u16 scissor_x2 = (u16)((regs.rasterizer.scissor_test.x2 + 1) << 4);
+ u16 scissor_y2 = (u16)((regs.rasterizer.scissor_test.y2 + 1) << 4);
- if (regs.scissor_test.mode == Regs::ScissorMode::Include) {
+ if (regs.rasterizer.scissor_test.mode == RasterizerRegs::ScissorMode::Include) {
// Calculate the new bounds
min_x = std::max(min_x, scissor_x1);
min_y = std::max(min_y, scissor_y1);
@@ -396,12 +164,13 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader
auto w_inverse = Math::MakeVec(v0.pos.w, v1.pos.w, v2.pos.w);
- auto textures = regs.GetTextures();
- auto tev_stages = regs.GetTevStages();
+ auto textures = regs.texturing.GetTextures();
+ auto tev_stages = regs.texturing.GetTevStages();
- bool stencil_action_enable = g_state.regs.output_merger.stencil_test.enable &&
- g_state.regs.framebuffer.depth_format == Regs::DepthFormat::D24S8;
- const auto stencil_test = g_state.regs.output_merger.stencil_test;
+ bool stencil_action_enable =
+ g_state.regs.framebuffer.output_merger.stencil_test.enable &&
+ g_state.regs.framebuffer.framebuffer.depth_format == FramebufferRegs::DepthFormat::D24S8;
+ const auto stencil_test = g_state.regs.framebuffer.output_merger.stencil_test;
// Enter rasterization loop, starting at the center of the topleft bounding box corner.
// TODO: Not sure if looping through x first might be faster
@@ -410,7 +179,7 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader
// Do not process the pixel if it's inside the scissor box and the scissor mode is set
// to Exclude
- if (regs.scissor_test.mode == Regs::ScissorMode::Exclude) {
+ if (regs.rasterizer.scissor_test.mode == RasterizerRegs::ScissorMode::Exclude) {
if (x >= scissor_x1 && x < scissor_x2 && y >= scissor_y1 && y < scissor_y2)
continue;
}
@@ -440,12 +209,14 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader
// Not fully accurate. About 3 bits in precision are missing.
// Z-Buffer (z / w * scale + offset)
- float depth_scale = float24::FromRaw(regs.viewport_depth_range).ToFloat32();
- float depth_offset = float24::FromRaw(regs.viewport_depth_near_plane).ToFloat32();
+ float depth_scale = float24::FromRaw(regs.rasterizer.viewport_depth_range).ToFloat32();
+ float depth_offset =
+ float24::FromRaw(regs.rasterizer.viewport_depth_near_plane).ToFloat32();
float depth = interpolated_z_over_w * depth_scale + depth_offset;
// Potentially switch to W-Buffer
- if (regs.depthmap_enable == Pica::Regs::DepthBuffering::WBuffering) {
+ if (regs.rasterizer.depthmap_enable ==
+ Pica::RasterizerRegs::DepthBuffering::WBuffering) {
// W-Buffer (z * scale + w * offset = (z / w * scale + offset) * w)
depth *= interpolated_w_inverse.ToFloat32() * wsum;
}
@@ -512,9 +283,9 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader
// TODO: Refactor so cubemaps and shadowmaps can be handled
if (i == 0) {
switch (texture.config.type) {
- case Regs::TextureConfig::Texture2D:
+ case TexturingRegs::TextureConfig::Texture2D:
break;
- case Regs::TextureConfig::Projection2D: {
+ case TexturingRegs::TextureConfig::Projection2D: {
auto tc0_w = GetInterpolatedAttribute(v0.tc0_w, v1.tc0_w, v2.tc0_w);
u /= tc0_w;
v /= tc0_w;
@@ -533,37 +304,9 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader
int t = (int)(v * float24::FromFloat32(static_cast<float>(texture.config.height)))
.ToFloat32();
- static auto GetWrappedTexCoord = [](Regs::TextureConfig::WrapMode mode, int val,
- unsigned size) {
- switch (mode) {
- case Regs::TextureConfig::ClampToEdge:
- val = std::max(val, 0);
- val = std::min(val, (int)size - 1);
- return val;
-
- case Regs::TextureConfig::ClampToBorder:
- return val;
-
- case Regs::TextureConfig::Repeat:
- return (int)((unsigned)val % size);
-
- case Regs::TextureConfig::MirroredRepeat: {
- unsigned int coord = ((unsigned)val % (2 * size));
- if (coord >= size)
- coord = 2 * size - 1 - coord;
- return (int)coord;
- }
-
- default:
- LOG_ERROR(HW_GPU, "Unknown texture coordinate wrapping mode %x", (int)mode);
- UNIMPLEMENTED();
- return 0;
- }
- };
-
- if ((texture.config.wrap_s == Regs::TextureConfig::ClampToBorder &&
+ if ((texture.config.wrap_s == TexturingRegs::TextureConfig::ClampToBorder &&
(s < 0 || static_cast<u32>(s) >= texture.config.width)) ||
- (texture.config.wrap_t == Regs::TextureConfig::ClampToBorder &&
+ (texture.config.wrap_t == TexturingRegs::TextureConfig::ClampToBorder &&
(t < 0 || static_cast<u32>(t) >= texture.config.height))) {
auto border_color = texture.config.border_color;
texture_color[i] = {border_color.r, border_color.g, border_color.b,
@@ -579,10 +322,10 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader
u8* texture_data =
Memory::GetPhysicalPointer(texture.config.GetPhysicalAddress());
auto info =
- DebugUtils::TextureInfo::FromPicaRegister(texture.config, texture.format);
+ Texture::TextureInfo::FromPicaRegister(texture.config, texture.format);
// TODO: Apply the min and mag filters to the texture
- texture_color[i] = DebugUtils::LookupTexture(texture_data, s, t, info);
+ texture_color[i] = Texture::LookupTexture(texture_data, s, t, info);
#if PICA_DUMP_TEXTURES
DebugUtils::DumpTexture(texture.config, texture_data);
#endif
@@ -599,17 +342,16 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader
Math::Vec4<u8> combiner_output;
Math::Vec4<u8> combiner_buffer = {0, 0, 0, 0};
Math::Vec4<u8> next_combiner_buffer = {
- regs.tev_combiner_buffer_color.r, regs.tev_combiner_buffer_color.g,
- regs.tev_combiner_buffer_color.b, regs.tev_combiner_buffer_color.a,
+ regs.texturing.tev_combiner_buffer_color.r,
+ regs.texturing.tev_combiner_buffer_color.g,
+ regs.texturing.tev_combiner_buffer_color.b,
+ regs.texturing.tev_combiner_buffer_color.a,
};
for (unsigned tev_stage_index = 0; tev_stage_index < tev_stages.size();
++tev_stage_index) {
const auto& tev_stage = tev_stages[tev_stage_index];
- using Source = Regs::TevStageConfig::Source;
- using ColorModifier = Regs::TevStageConfig::ColorModifier;
- using AlphaModifier = Regs::TevStageConfig::AlphaModifier;
- using Operation = Regs::TevStageConfig::Operation;
+ using Source = TexturingRegs::TevStageConfig::Source;
auto GetSource = [&](Source source) -> Math::Vec4<u8> {
switch (source) {
@@ -649,187 +391,6 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader
}
};
- static auto GetColorModifier = [](ColorModifier factor,
- const Math::Vec4<u8>& values) -> Math::Vec3<u8> {
- switch (factor) {
- case ColorModifier::SourceColor:
- return values.rgb();
-
- case ColorModifier::OneMinusSourceColor:
- return (Math::Vec3<u8>(255, 255, 255) - values.rgb()).Cast<u8>();
-
- case ColorModifier::SourceAlpha:
- return values.aaa();
-
- case ColorModifier::OneMinusSourceAlpha:
- return (Math::Vec3<u8>(255, 255, 255) - values.aaa()).Cast<u8>();
-
- case ColorModifier::SourceRed:
- return values.rrr();
-
- case ColorModifier::OneMinusSourceRed:
- return (Math::Vec3<u8>(255, 255, 255) - values.rrr()).Cast<u8>();
-
- case ColorModifier::SourceGreen:
- return values.ggg();
-
- case ColorModifier::OneMinusSourceGreen:
- return (Math::Vec3<u8>(255, 255, 255) - values.ggg()).Cast<u8>();
-
- case ColorModifier::SourceBlue:
- return values.bbb();
-
- case ColorModifier::OneMinusSourceBlue:
- return (Math::Vec3<u8>(255, 255, 255) - values.bbb()).Cast<u8>();
- }
- };
-
- static auto GetAlphaModifier = [](AlphaModifier factor,
- const Math::Vec4<u8>& values) -> u8 {
- switch (factor) {
- case AlphaModifier::SourceAlpha:
- return values.a();
-
- case AlphaModifier::OneMinusSourceAlpha:
- return 255 - values.a();
-
- case AlphaModifier::SourceRed:
- return values.r();
-
- case AlphaModifier::OneMinusSourceRed:
- return 255 - values.r();
-
- case AlphaModifier::SourceGreen:
- return values.g();
-
- case AlphaModifier::OneMinusSourceGreen:
- return 255 - values.g();
-
- case AlphaModifier::SourceBlue:
- return values.b();
-
- case AlphaModifier::OneMinusSourceBlue:
- return 255 - values.b();
- }
- };
-
- static auto ColorCombine = [](Operation op,
- const Math::Vec3<u8> input[3]) -> Math::Vec3<u8> {
- switch (op) {
- case Operation::Replace:
- return input[0];
-
- case Operation::Modulate:
- return ((input[0] * input[1]) / 255).Cast<u8>();
-
- case Operation::Add: {
- auto result = input[0] + input[1];
- result.r() = std::min(255, result.r());
- result.g() = std::min(255, result.g());
- result.b() = std::min(255, result.b());
- return result.Cast<u8>();
- }
-
- case Operation::AddSigned: {
- // TODO(bunnei): Verify that the color conversion from (float) 0.5f to
- // (byte) 128 is correct
- auto result = input[0].Cast<int>() + input[1].Cast<int>() -
- Math::MakeVec<int>(128, 128, 128);
- result.r() = MathUtil::Clamp<int>(result.r(), 0, 255);
- result.g() = MathUtil::Clamp<int>(result.g(), 0, 255);
- result.b() = MathUtil::Clamp<int>(result.b(), 0, 255);
- return result.Cast<u8>();
- }
-
- case Operation::Lerp:
- return ((input[0] * input[2] +
- input[1] *
- (Math::MakeVec<u8>(255, 255, 255) - input[2]).Cast<u8>()) /
- 255)
- .Cast<u8>();
-
- case Operation::Subtract: {
- auto result = input[0].Cast<int>() - input[1].Cast<int>();
- result.r() = std::max(0, result.r());
- result.g() = std::max(0, result.g());
- result.b() = std::max(0, result.b());
- return result.Cast<u8>();
- }
-
- case Operation::MultiplyThenAdd: {
- auto result = (input[0] * input[1] + 255 * input[2].Cast<int>()) / 255;
- result.r() = std::min(255, result.r());
- result.g() = std::min(255, result.g());
- result.b() = std::min(255, result.b());
- return result.Cast<u8>();
- }
-
- case Operation::AddThenMultiply: {
- auto result = input[0] + input[1];
- result.r() = std::min(255, result.r());
- result.g() = std::min(255, result.g());
- result.b() = std::min(255, result.b());
- result = (result * input[2].Cast<int>()) / 255;
- return result.Cast<u8>();
- }
- case Operation::Dot3_RGB: {
- // Not fully accurate.
- // Worst case scenario seems to yield a +/-3 error
- // Some HW results indicate that the per-component computation can't have a
- // higher precision than 1/256,
- // while dot3_rgb( (0x80,g0,b0),(0x7F,g1,b1) ) and dot3_rgb(
- // (0x80,g0,b0),(0x80,g1,b1) ) give different results
- int result =
- ((input[0].r() * 2 - 255) * (input[1].r() * 2 - 255) + 128) / 256 +
- ((input[0].g() * 2 - 255) * (input[1].g() * 2 - 255) + 128) / 256 +
- ((input[0].b() * 2 - 255) * (input[1].b() * 2 - 255) + 128) / 256;
- result = std::max(0, std::min(255, result));
- return {(u8)result, (u8)result, (u8)result};
- }
- default:
- LOG_ERROR(HW_GPU, "Unknown color combiner operation %d", (int)op);
- UNIMPLEMENTED();
- return {0, 0, 0};
- }
- };
-
- static auto AlphaCombine = [](Operation op, const std::array<u8, 3>& input) -> u8 {
- switch (op) {
- case Operation::Replace:
- return input[0];
-
- case Operation::Modulate:
- return input[0] * input[1] / 255;
-
- case Operation::Add:
- return std::min(255, input[0] + input[1]);
-
- case Operation::AddSigned: {
- // TODO(bunnei): Verify that the color conversion from (float) 0.5f to
- // (byte) 128 is correct
- auto result = static_cast<int>(input[0]) + static_cast<int>(input[1]) - 128;
- return static_cast<u8>(MathUtil::Clamp<int>(result, 0, 255));
- }
-
- case Operation::Lerp:
- return (input[0] * input[2] + input[1] * (255 - input[2])) / 255;
-
- case Operation::Subtract:
- return std::max(0, (int)input[0] - (int)input[1]);
-
- case Operation::MultiplyThenAdd:
- return std::min(255, (input[0] * input[1] + 255 * input[2]) / 255);
-
- case Operation::AddThenMultiply:
- return (std::min(255, (input[0] + input[1])) * input[2]) / 255;
-
- default:
- LOG_ERROR(HW_GPU, "Unknown alpha combiner operation %d", (int)op);
- UNIMPLEMENTED();
- return 0;
- }
- };
-
// color combiner
// NOTE: Not sure if the alpha combiner might use the color output of the previous
// stage as input. Hence, we currently don't directly write the result to
@@ -861,54 +422,54 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader
combiner_buffer = next_combiner_buffer;
- if (regs.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferColor(
+ if (regs.texturing.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferColor(
tev_stage_index)) {
next_combiner_buffer.r() = combiner_output.r();
next_combiner_buffer.g() = combiner_output.g();
next_combiner_buffer.b() = combiner_output.b();
}
- if (regs.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferAlpha(
+ if (regs.texturing.tev_combiner_buffer_input.TevStageUpdatesCombinerBufferAlpha(
tev_stage_index)) {
next_combiner_buffer.a() = combiner_output.a();
}
}
- const auto& output_merger = regs.output_merger;
+ const auto& output_merger = regs.framebuffer.output_merger;
// TODO: Does alpha testing happen before or after stencil?
if (output_merger.alpha_test.enable) {
bool pass = false;
switch (output_merger.alpha_test.func) {
- case Regs::CompareFunc::Never:
+ case FramebufferRegs::CompareFunc::Never:
pass = false;
break;
- case Regs::CompareFunc::Always:
+ case FramebufferRegs::CompareFunc::Always:
pass = true;
break;
- case Regs::CompareFunc::Equal:
+ case FramebufferRegs::CompareFunc::Equal:
pass = combiner_output.a() == output_merger.alpha_test.ref;
break;
- case Regs::CompareFunc::NotEqual:
+ case FramebufferRegs::CompareFunc::NotEqual:
pass = combiner_output.a() != output_merger.alpha_test.ref;
break;
- case Regs::CompareFunc::LessThan:
+ case FramebufferRegs::CompareFunc::LessThan:
pass = combiner_output.a() < output_merger.alpha_test.ref;
break;
- case Regs::CompareFunc::LessThanOrEqual:
+ case FramebufferRegs::CompareFunc::LessThanOrEqual:
pass = combiner_output.a() <= output_merger.alpha_test.ref;
break;
- case Regs::CompareFunc::GreaterThan:
+ case FramebufferRegs::CompareFunc::GreaterThan:
pass = combiner_output.a() > output_merger.alpha_test.ref;
break;
- case Regs::CompareFunc::GreaterThanOrEqual:
+ case FramebufferRegs::CompareFunc::GreaterThanOrEqual:
pass = combiner_output.a() >= output_merger.alpha_test.ref;
break;
}
@@ -921,16 +482,16 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader
// Not fully accurate. We'd have to know what data type is used to
// store the depth etc. Using float for now until we know more
// about Pica datatypes
- if (regs.fog_mode == Regs::FogMode::Fog) {
+ if (regs.texturing.fog_mode == TexturingRegs::FogMode::Fog) {
const Math::Vec3<u8> fog_color = {
- static_cast<u8>(regs.fog_color.r.Value()),
- static_cast<u8>(regs.fog_color.g.Value()),
- static_cast<u8>(regs.fog_color.b.Value()),
+ static_cast<u8>(regs.texturing.fog_color.r.Value()),
+ static_cast<u8>(regs.texturing.fog_color.g.Value()),
+ static_cast<u8>(regs.texturing.fog_color.b.Value()),
};
// Get index into fog LUT
float fog_index;
- if (g_state.regs.fog_flip) {
+ if (g_state.regs.texturing.fog_flip) {
fog_index = (1.0f - depth) * 128.0f;
} else {
fog_index = depth * 128.0f;
@@ -954,10 +515,10 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader
u8 old_stencil = 0;
auto UpdateStencil = [stencil_test, x, y,
- &old_stencil](Pica::Regs::StencilAction action) {
+ &old_stencil](Pica::FramebufferRegs::StencilAction action) {
u8 new_stencil =
PerformStencilAction(action, old_stencil, stencil_test.reference_value);
- if (g_state.regs.framebuffer.allow_depth_stencil_write != 0)
+ if (g_state.regs.framebuffer.framebuffer.allow_depth_stencil_write != 0)
SetStencil(x >> 4, y >> 4, (new_stencil & stencil_test.write_mask) |
(old_stencil & ~stencil_test.write_mask));
};
@@ -969,35 +530,35 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader
bool pass = false;
switch (stencil_test.func) {
- case Regs::CompareFunc::Never:
+ case FramebufferRegs::CompareFunc::Never:
pass = false;
break;
- case Regs::CompareFunc::Always:
+ case FramebufferRegs::CompareFunc::Always:
pass = true;
break;
- case Regs::CompareFunc::Equal:
+ case FramebufferRegs::CompareFunc::Equal:
pass = (ref == dest);
break;
- case Regs::CompareFunc::NotEqual:
+ case FramebufferRegs::CompareFunc::NotEqual:
pass = (ref != dest);
break;
- case Regs::CompareFunc::LessThan:
+ case FramebufferRegs::CompareFunc::LessThan:
pass = (ref < dest);
break;
- case Regs::CompareFunc::LessThanOrEqual:
+ case FramebufferRegs::CompareFunc::LessThanOrEqual:
pass = (ref <= dest);
break;
- case Regs::CompareFunc::GreaterThan:
+ case FramebufferRegs::CompareFunc::GreaterThan:
pass = (ref > dest);
break;
- case Regs::CompareFunc::GreaterThanOrEqual:
+ case FramebufferRegs::CompareFunc::GreaterThanOrEqual:
pass = (ref >= dest);
break;
}
@@ -1009,7 +570,8 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader
}
// Convert float to integer
- unsigned num_bits = Regs::DepthBitsPerPixel(regs.framebuffer.depth_format);
+ unsigned num_bits =
+ FramebufferRegs::DepthBitsPerPixel(regs.framebuffer.framebuffer.depth_format);
u32 z = (u32)(depth * ((1 << num_bits) - 1));
if (output_merger.depth_test_enable) {
@@ -1018,35 +580,35 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader
bool pass = false;
switch (output_merger.depth_test_func) {
- case Regs::CompareFunc::Never:
+ case FramebufferRegs::CompareFunc::Never:
pass = false;
break;
- case Regs::CompareFunc::Always:
+ case FramebufferRegs::CompareFunc::Always:
pass = true;
break;
- case Regs::CompareFunc::Equal:
+ case FramebufferRegs::CompareFunc::Equal:
pass = z == ref_z;
break;
- case Regs::CompareFunc::NotEqual:
+ case FramebufferRegs::CompareFunc::NotEqual:
pass = z != ref_z;
break;
- case Regs::CompareFunc::LessThan:
+ case FramebufferRegs::CompareFunc::LessThan:
pass = z < ref_z;
break;
- case Regs::CompareFunc::LessThanOrEqual:
+ case FramebufferRegs::CompareFunc::LessThanOrEqual:
pass = z <= ref_z;
break;
- case Regs::CompareFunc::GreaterThan:
+ case FramebufferRegs::CompareFunc::GreaterThan:
pass = z > ref_z;
break;
- case Regs::CompareFunc::GreaterThanOrEqual:
+ case FramebufferRegs::CompareFunc::GreaterThanOrEqual:
pass = z >= ref_z;
break;
}
@@ -1058,8 +620,11 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader
}
}
- if (regs.framebuffer.allow_depth_stencil_write != 0 && output_merger.depth_write_enable)
+ if (regs.framebuffer.framebuffer.allow_depth_stencil_write != 0 &&
+ output_merger.depth_write_enable) {
+
SetDepth(x >> 4, y >> 4, z);
+ }
// The stencil depth_pass action is executed even if depth testing is disabled
if (stencil_action_enable)
@@ -1071,7 +636,8 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader
if (output_merger.alphablend_enable) {
auto params = output_merger.alpha_blending;
- auto LookupFactor = [&](unsigned channel, Regs::BlendFactor factor) -> u8 {
+ auto LookupFactor = [&](unsigned channel,
+ FramebufferRegs::BlendFactor factor) -> u8 {
DEBUG_ASSERT(channel < 4);
const Math::Vec4<u8> blend_const = {
@@ -1082,49 +648,49 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader
};
switch (factor) {
- case Regs::BlendFactor::Zero:
+ case FramebufferRegs::BlendFactor::Zero:
return 0;
- case Regs::BlendFactor::One:
+ case FramebufferRegs::BlendFactor::One:
return 255;
- case Regs::BlendFactor::SourceColor:
+ case FramebufferRegs::BlendFactor::SourceColor:
return combiner_output[channel];
- case Regs::BlendFactor::OneMinusSourceColor:
+ case FramebufferRegs::BlendFactor::OneMinusSourceColor:
return 255 - combiner_output[channel];
- case Regs::BlendFactor::DestColor:
+ case FramebufferRegs::BlendFactor::DestColor:
return dest[channel];
- case Regs::BlendFactor::OneMinusDestColor:
+ case FramebufferRegs::BlendFactor::OneMinusDestColor:
return 255 - dest[channel];
- case Regs::BlendFactor::SourceAlpha:
+ case FramebufferRegs::BlendFactor::SourceAlpha:
return combiner_output.a();
- case Regs::BlendFactor::OneMinusSourceAlpha:
+ case FramebufferRegs::BlendFactor::OneMinusSourceAlpha:
return 255 - combiner_output.a();
- case Regs::BlendFactor::DestAlpha:
+ case FramebufferRegs::BlendFactor::DestAlpha:
return dest.a();
- case Regs::BlendFactor::OneMinusDestAlpha:
+ case FramebufferRegs::BlendFactor::OneMinusDestAlpha:
return 255 - dest.a();
- case Regs::BlendFactor::ConstantColor:
+ case FramebufferRegs::BlendFactor::ConstantColor:
return blend_const[channel];
- case Regs::BlendFactor::OneMinusConstantColor:
+ case FramebufferRegs::BlendFactor::OneMinusConstantColor:
return 255 - blend_const[channel];
- case Regs::BlendFactor::ConstantAlpha:
+ case FramebufferRegs::BlendFactor::ConstantAlpha:
return blend_const.a();
- case Regs::BlendFactor::OneMinusConstantAlpha:
+ case FramebufferRegs::BlendFactor::OneMinusConstantAlpha:
return 255 - blend_const.a();
- case Regs::BlendFactor::SourceAlphaSaturate:
+ case FramebufferRegs::BlendFactor::SourceAlphaSaturate:
// Returns 1.0 for the alpha channel
if (channel == 3)
return 255;
@@ -1139,55 +705,6 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader
return combiner_output[channel];
};
- static auto EvaluateBlendEquation = [](
- const Math::Vec4<u8>& src, const Math::Vec4<u8>& srcfactor,
- const Math::Vec4<u8>& dest, const Math::Vec4<u8>& destfactor,
- Regs::BlendEquation equation) {
- Math::Vec4<int> result;
-
- auto src_result = (src * srcfactor).Cast<int>();
- auto dst_result = (dest * destfactor).Cast<int>();
-
- switch (equation) {
- case Regs::BlendEquation::Add:
- result = (src_result + dst_result) / 255;
- break;
-
- case Regs::BlendEquation::Subtract:
- result = (src_result - dst_result) / 255;
- break;
-
- case Regs::BlendEquation::ReverseSubtract:
- result = (dst_result - src_result) / 255;
- break;
-
- // TODO: How do these two actually work?
- // OpenGL doesn't include the blend factors in the min/max computations,
- // but is this what the 3DS actually does?
- case Regs::BlendEquation::Min:
- result.r() = std::min(src.r(), dest.r());
- result.g() = std::min(src.g(), dest.g());
- result.b() = std::min(src.b(), dest.b());
- result.a() = std::min(src.a(), dest.a());
- break;
-
- case Regs::BlendEquation::Max:
- result.r() = std::max(src.r(), dest.r());
- result.g() = std::max(src.g(), dest.g());
- result.b() = std::max(src.b(), dest.b());
- result.a() = std::max(src.a(), dest.a());
- break;
-
- default:
- LOG_CRITICAL(HW_GPU, "Unknown RGB blend equation %x", equation);
- UNIMPLEMENTED();
- }
-
- return Math::Vec4<u8>(
- MathUtil::Clamp(result.r(), 0, 255), MathUtil::Clamp(result.g(), 0, 255),
- MathUtil::Clamp(result.b(), 0, 255), MathUtil::Clamp(result.a(), 0, 255));
- };
-
auto srcfactor = Math::MakeVec(LookupFactor(0, params.factor_source_rgb),
LookupFactor(1, params.factor_source_rgb),
LookupFactor(2, params.factor_source_rgb),
@@ -1204,58 +721,6 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader
dstfactor, params.blend_equation_a)
.a();
} else {
- static auto LogicOp = [](u8 src, u8 dest, Regs::LogicOp op) -> u8 {
- switch (op) {
- case Regs::LogicOp::Clear:
- return 0;
-
- case Regs::LogicOp::And:
- return src & dest;
-
- case Regs::LogicOp::AndReverse:
- return src & ~dest;
-
- case Regs::LogicOp::Copy:
- return src;
-
- case Regs::LogicOp::Set:
- return 255;
-
- case Regs::LogicOp::CopyInverted:
- return ~src;
-
- case Regs::LogicOp::NoOp:
- return dest;
-
- case Regs::LogicOp::Invert:
- return ~dest;
-
- case Regs::LogicOp::Nand:
- return ~(src & dest);
-
- case Regs::LogicOp::Or:
- return src | dest;
-
- case Regs::LogicOp::Nor:
- return ~(src | dest);
-
- case Regs::LogicOp::Xor:
- return src ^ dest;
-
- case Regs::LogicOp::Equiv:
- return ~(src ^ dest);
-
- case Regs::LogicOp::AndInverted:
- return ~src & dest;
-
- case Regs::LogicOp::OrReverse:
- return src | ~dest;
-
- case Regs::LogicOp::OrInverted:
- return ~src | dest;
- }
- };
-
blend_output =
Math::MakeVec(LogicOp(combiner_output.r(), dest.r(), output_merger.logic_op),
LogicOp(combiner_output.g(), dest.g(), output_merger.logic_op),
@@ -1270,14 +735,13 @@ static void ProcessTriangleInternal(const Shader::OutputVertex& v0, const Shader
output_merger.alpha_enable ? blend_output.a() : dest.a(),
};
- if (regs.framebuffer.allow_color_write != 0)
+ if (regs.framebuffer.framebuffer.allow_color_write != 0)
DrawPixel(x >> 4, y >> 4, result);
}
}
}
-void ProcessTriangle(const Shader::OutputVertex& v0, const Shader::OutputVertex& v1,
- const Shader::OutputVertex& v2) {
+void ProcessTriangle(const Vertex& v0, const Vertex& v1, const Vertex& v2) {
ProcessTriangleInternal(v0, v1, v2);
}
diff --git a/src/video_core/swrasterizer/rasterizer.h b/src/video_core/swrasterizer/rasterizer.h
new file mode 100644
index 000000000..3a72ac343
--- /dev/null
+++ b/src/video_core/swrasterizer/rasterizer.h
@@ -0,0 +1,48 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "video_core/shader/shader.h"
+
+namespace Pica {
+
+namespace Rasterizer {
+
+struct Vertex : Shader::OutputVertex {
+ Vertex(const OutputVertex& v) : OutputVertex(v) {}
+
+ // Attributes used to store intermediate results
+ // position after perspective divide
+ Math::Vec3<float24> screenpos;
+
+ // Linear interpolation
+ // factor: 0=this, 1=vtx
+ void Lerp(float24 factor, const Vertex& vtx) {
+ pos = pos * factor + vtx.pos * (float24::FromFloat32(1) - factor);
+
+ // TODO: Should perform perspective correct interpolation here...
+ tc0 = tc0 * factor + vtx.tc0 * (float24::FromFloat32(1) - factor);
+ tc1 = tc1 * factor + vtx.tc1 * (float24::FromFloat32(1) - factor);
+ tc2 = tc2 * factor + vtx.tc2 * (float24::FromFloat32(1) - factor);
+
+ screenpos = screenpos * factor + vtx.screenpos * (float24::FromFloat32(1) - factor);
+
+ color = color * factor + vtx.color * (float24::FromFloat32(1) - factor);
+ }
+
+ // Linear interpolation
+ // factor: 0=v0, 1=v1
+ static Vertex Lerp(float24 factor, const Vertex& v0, const Vertex& v1) {
+ Vertex ret = v0;
+ ret.Lerp(factor, v1);
+ return ret;
+ }
+};
+
+void ProcessTriangle(const Vertex& v0, const Vertex& v1, const Vertex& v2);
+
+} // namespace Rasterizer
+
+} // namespace Pica
diff --git a/src/video_core/swrasterizer.cpp b/src/video_core/swrasterizer/swrasterizer.cpp
index 9cd21f72b..402b705dd 100644
--- a/src/video_core/swrasterizer.cpp
+++ b/src/video_core/swrasterizer/swrasterizer.cpp
@@ -2,8 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "video_core/clipper.h"
-#include "video_core/swrasterizer.h"
+#include "video_core/swrasterizer/clipper.h"
+#include "video_core/swrasterizer/swrasterizer.h"
namespace VideoCore {
diff --git a/src/video_core/swrasterizer.h b/src/video_core/swrasterizer/swrasterizer.h
index 6d42d7409..6d42d7409 100644
--- a/src/video_core/swrasterizer.h
+++ b/src/video_core/swrasterizer/swrasterizer.h
diff --git a/src/video_core/swrasterizer/texturing.cpp b/src/video_core/swrasterizer/texturing.cpp
new file mode 100644
index 000000000..eb18e4ba4
--- /dev/null
+++ b/src/video_core/swrasterizer/texturing.cpp
@@ -0,0 +1,228 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "common/math_util.h"
+#include "common/vector_math.h"
+#include "video_core/regs_texturing.h"
+#include "video_core/swrasterizer/texturing.h"
+
+namespace Pica {
+namespace Rasterizer {
+
+using TevStageConfig = TexturingRegs::TevStageConfig;
+
+int GetWrappedTexCoord(TexturingRegs::TextureConfig::WrapMode mode, int val, unsigned size) {
+ switch (mode) {
+ case TexturingRegs::TextureConfig::ClampToEdge:
+ val = std::max(val, 0);
+ val = std::min(val, (int)size - 1);
+ return val;
+
+ case TexturingRegs::TextureConfig::ClampToBorder:
+ return val;
+
+ case TexturingRegs::TextureConfig::Repeat:
+ return (int)((unsigned)val % size);
+
+ case TexturingRegs::TextureConfig::MirroredRepeat: {
+ unsigned int coord = ((unsigned)val % (2 * size));
+ if (coord >= size)
+ coord = 2 * size - 1 - coord;
+ return (int)coord;
+ }
+
+ default:
+ LOG_ERROR(HW_GPU, "Unknown texture coordinate wrapping mode %x", (int)mode);
+ UNIMPLEMENTED();
+ return 0;
+ }
+};
+
+Math::Vec3<u8> GetColorModifier(TevStageConfig::ColorModifier factor,
+ const Math::Vec4<u8>& values) {
+ using ColorModifier = TevStageConfig::ColorModifier;
+
+ switch (factor) {
+ case ColorModifier::SourceColor:
+ return values.rgb();
+
+ case ColorModifier::OneMinusSourceColor:
+ return (Math::Vec3<u8>(255, 255, 255) - values.rgb()).Cast<u8>();
+
+ case ColorModifier::SourceAlpha:
+ return values.aaa();
+
+ case ColorModifier::OneMinusSourceAlpha:
+ return (Math::Vec3<u8>(255, 255, 255) - values.aaa()).Cast<u8>();
+
+ case ColorModifier::SourceRed:
+ return values.rrr();
+
+ case ColorModifier::OneMinusSourceRed:
+ return (Math::Vec3<u8>(255, 255, 255) - values.rrr()).Cast<u8>();
+
+ case ColorModifier::SourceGreen:
+ return values.ggg();
+
+ case ColorModifier::OneMinusSourceGreen:
+ return (Math::Vec3<u8>(255, 255, 255) - values.ggg()).Cast<u8>();
+
+ case ColorModifier::SourceBlue:
+ return values.bbb();
+
+ case ColorModifier::OneMinusSourceBlue:
+ return (Math::Vec3<u8>(255, 255, 255) - values.bbb()).Cast<u8>();
+ }
+};
+
+u8 GetAlphaModifier(TevStageConfig::AlphaModifier factor, const Math::Vec4<u8>& values) {
+ using AlphaModifier = TevStageConfig::AlphaModifier;
+
+ switch (factor) {
+ case AlphaModifier::SourceAlpha:
+ return values.a();
+
+ case AlphaModifier::OneMinusSourceAlpha:
+ return 255 - values.a();
+
+ case AlphaModifier::SourceRed:
+ return values.r();
+
+ case AlphaModifier::OneMinusSourceRed:
+ return 255 - values.r();
+
+ case AlphaModifier::SourceGreen:
+ return values.g();
+
+ case AlphaModifier::OneMinusSourceGreen:
+ return 255 - values.g();
+
+ case AlphaModifier::SourceBlue:
+ return values.b();
+
+ case AlphaModifier::OneMinusSourceBlue:
+ return 255 - values.b();
+ }
+};
+
+Math::Vec3<u8> ColorCombine(TevStageConfig::Operation op, const Math::Vec3<u8> input[3]) {
+ using Operation = TevStageConfig::Operation;
+
+ switch (op) {
+ case Operation::Replace:
+ return input[0];
+
+ case Operation::Modulate:
+ return ((input[0] * input[1]) / 255).Cast<u8>();
+
+ case Operation::Add: {
+ auto result = input[0] + input[1];
+ result.r() = std::min(255, result.r());
+ result.g() = std::min(255, result.g());
+ result.b() = std::min(255, result.b());
+ return result.Cast<u8>();
+ }
+
+ case Operation::AddSigned: {
+ // TODO(bunnei): Verify that the color conversion from (float) 0.5f to
+ // (byte) 128 is correct
+ auto result =
+ input[0].Cast<int>() + input[1].Cast<int>() - Math::MakeVec<int>(128, 128, 128);
+ result.r() = MathUtil::Clamp<int>(result.r(), 0, 255);
+ result.g() = MathUtil::Clamp<int>(result.g(), 0, 255);
+ result.b() = MathUtil::Clamp<int>(result.b(), 0, 255);
+ return result.Cast<u8>();
+ }
+
+ case Operation::Lerp:
+ return ((input[0] * input[2] +
+ input[1] * (Math::MakeVec<u8>(255, 255, 255) - input[2]).Cast<u8>()) /
+ 255)
+ .Cast<u8>();
+
+ case Operation::Subtract: {
+ auto result = input[0].Cast<int>() - input[1].Cast<int>();
+ result.r() = std::max(0, result.r());
+ result.g() = std::max(0, result.g());
+ result.b() = std::max(0, result.b());
+ return result.Cast<u8>();
+ }
+
+ case Operation::MultiplyThenAdd: {
+ auto result = (input[0] * input[1] + 255 * input[2].Cast<int>()) / 255;
+ result.r() = std::min(255, result.r());
+ result.g() = std::min(255, result.g());
+ result.b() = std::min(255, result.b());
+ return result.Cast<u8>();
+ }
+
+ case Operation::AddThenMultiply: {
+ auto result = input[0] + input[1];
+ result.r() = std::min(255, result.r());
+ result.g() = std::min(255, result.g());
+ result.b() = std::min(255, result.b());
+ result = (result * input[2].Cast<int>()) / 255;
+ return result.Cast<u8>();
+ }
+ case Operation::Dot3_RGB: {
+ // Not fully accurate. Worst case scenario seems to yield a +/-3 error. Some HW results
+ // indicate that the per-component computation can't have a higher precision than 1/256,
+ // while dot3_rgb((0x80,g0,b0), (0x7F,g1,b1)) and dot3_rgb((0x80,g0,b0), (0x80,g1,b1)) give
+ // different results.
+ int result = ((input[0].r() * 2 - 255) * (input[1].r() * 2 - 255) + 128) / 256 +
+ ((input[0].g() * 2 - 255) * (input[1].g() * 2 - 255) + 128) / 256 +
+ ((input[0].b() * 2 - 255) * (input[1].b() * 2 - 255) + 128) / 256;
+ result = std::max(0, std::min(255, result));
+ return {(u8)result, (u8)result, (u8)result};
+ }
+ default:
+ LOG_ERROR(HW_GPU, "Unknown color combiner operation %d", (int)op);
+ UNIMPLEMENTED();
+ return {0, 0, 0};
+ }
+};
+
+u8 AlphaCombine(TevStageConfig::Operation op, const std::array<u8, 3>& input) {
+ switch (op) {
+ using Operation = TevStageConfig::Operation;
+ case Operation::Replace:
+ return input[0];
+
+ case Operation::Modulate:
+ return input[0] * input[1] / 255;
+
+ case Operation::Add:
+ return std::min(255, input[0] + input[1]);
+
+ case Operation::AddSigned: {
+ // TODO(bunnei): Verify that the color conversion from (float) 0.5f to (byte) 128 is correct
+ auto result = static_cast<int>(input[0]) + static_cast<int>(input[1]) - 128;
+ return static_cast<u8>(MathUtil::Clamp<int>(result, 0, 255));
+ }
+
+ case Operation::Lerp:
+ return (input[0] * input[2] + input[1] * (255 - input[2])) / 255;
+
+ case Operation::Subtract:
+ return std::max(0, (int)input[0] - (int)input[1]);
+
+ case Operation::MultiplyThenAdd:
+ return std::min(255, (input[0] * input[1] + 255 * input[2]) / 255);
+
+ case Operation::AddThenMultiply:
+ return (std::min(255, (input[0] + input[1])) * input[2]) / 255;
+
+ default:
+ LOG_ERROR(HW_GPU, "Unknown alpha combiner operation %d", (int)op);
+ UNIMPLEMENTED();
+ return 0;
+ }
+};
+
+} // namespace Rasterizer
+} // namespace Pica
diff --git a/src/video_core/swrasterizer/texturing.h b/src/video_core/swrasterizer/texturing.h
new file mode 100644
index 000000000..24f74a5a3
--- /dev/null
+++ b/src/video_core/swrasterizer/texturing.h
@@ -0,0 +1,28 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "common/vector_math.h"
+#include "video_core/regs_texturing.h"
+
+namespace Pica {
+namespace Rasterizer {
+
+int GetWrappedTexCoord(TexturingRegs::TextureConfig::WrapMode mode, int val, unsigned size);
+
+Math::Vec3<u8> GetColorModifier(TexturingRegs::TevStageConfig::ColorModifier factor,
+ const Math::Vec4<u8>& values);
+
+u8 GetAlphaModifier(TexturingRegs::TevStageConfig::AlphaModifier factor,
+ const Math::Vec4<u8>& values);
+
+Math::Vec3<u8> ColorCombine(TexturingRegs::TevStageConfig::Operation op,
+ const Math::Vec3<u8> input[3]);
+
+u8 AlphaCombine(TexturingRegs::TevStageConfig::Operation op, const std::array<u8, 3>& input);
+
+} // namespace Rasterizer
+} // namespace Pica
diff --git a/src/video_core/texture/etc1.cpp b/src/video_core/texture/etc1.cpp
new file mode 100644
index 000000000..43f7f56db
--- /dev/null
+++ b/src/video_core/texture/etc1.cpp
@@ -0,0 +1,122 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <array>
+#include "common/bit_field.h"
+#include "common/color.h"
+#include "common/common_types.h"
+#include "common/math_util.h"
+#include "common/vector_math.h"
+#include "video_core/texture/etc1.h"
+
+namespace Pica {
+namespace Texture {
+
+namespace {
+
+constexpr std::array<std::array<u8, 2>, 8> etc1_modifier_table = {{
+ {2, 8}, {5, 17}, {9, 29}, {13, 42}, {18, 60}, {24, 80}, {33, 106}, {47, 183},
+}};
+
+union ETC1Tile {
+ u64 raw;
+
+ // Each of these two is a collection of 16 bits (one per lookup value)
+ BitField<0, 16, u64> table_subindexes;
+ BitField<16, 16, u64> negation_flags;
+
+ unsigned GetTableSubIndex(unsigned index) const {
+ return (table_subindexes >> index) & 1;
+ }
+
+ bool GetNegationFlag(unsigned index) const {
+ return ((negation_flags >> index) & 1) == 1;
+ }
+
+ BitField<32, 1, u64> flip;
+ BitField<33, 1, u64> differential_mode;
+
+ BitField<34, 3, u64> table_index_2;
+ BitField<37, 3, u64> table_index_1;
+
+ union {
+ // delta value + base value
+ BitField<40, 3, s64> db;
+ BitField<43, 5, u64> b;
+
+ BitField<48, 3, s64> dg;
+ BitField<51, 5, u64> g;
+
+ BitField<56, 3, s64> dr;
+ BitField<59, 5, u64> r;
+ } differential;
+
+ union {
+ BitField<40, 4, u64> b2;
+ BitField<44, 4, u64> b1;
+
+ BitField<48, 4, u64> g2;
+ BitField<52, 4, u64> g1;
+
+ BitField<56, 4, u64> r2;
+ BitField<60, 4, u64> r1;
+ } separate;
+
+ const Math::Vec3<u8> GetRGB(unsigned int x, unsigned int y) const {
+ int texel = 4 * x + y;
+
+ if (flip)
+ std::swap(x, y);
+
+ // Lookup base value
+ Math::Vec3<int> ret;
+ if (differential_mode) {
+ ret.r() = static_cast<int>(differential.r);
+ ret.g() = static_cast<int>(differential.g);
+ ret.b() = static_cast<int>(differential.b);
+ if (x >= 2) {
+ ret.r() += static_cast<int>(differential.dr);
+ ret.g() += static_cast<int>(differential.dg);
+ ret.b() += static_cast<int>(differential.db);
+ }
+ ret.r() = Color::Convert5To8(ret.r());
+ ret.g() = Color::Convert5To8(ret.g());
+ ret.b() = Color::Convert5To8(ret.b());
+ } else {
+ if (x < 2) {
+ ret.r() = Color::Convert4To8(static_cast<u8>(separate.r1));
+ ret.g() = Color::Convert4To8(static_cast<u8>(separate.g1));
+ ret.b() = Color::Convert4To8(static_cast<u8>(separate.b1));
+ } else {
+ ret.r() = Color::Convert4To8(static_cast<u8>(separate.r2));
+ ret.g() = Color::Convert4To8(static_cast<u8>(separate.g2));
+ ret.b() = Color::Convert4To8(static_cast<u8>(separate.b2));
+ }
+ }
+
+ // Add modifier
+ unsigned table_index =
+ static_cast<int>((x < 2) ? table_index_1.Value() : table_index_2.Value());
+
+ int modifier = etc1_modifier_table[table_index][GetTableSubIndex(texel)];
+ if (GetNegationFlag(texel))
+ modifier *= -1;
+
+ ret.r() = MathUtil::Clamp(ret.r() + modifier, 0, 255);
+ ret.g() = MathUtil::Clamp(ret.g() + modifier, 0, 255);
+ ret.b() = MathUtil::Clamp(ret.b() + modifier, 0, 255);
+
+ return ret.Cast<u8>();
+ }
+};
+
+} // anonymous namespace
+
+Math::Vec3<u8> SampleETC1Subtile(u64 value, unsigned int x, unsigned int y) {
+ ETC1Tile tile{value};
+ return tile.GetRGB(x, y);
+}
+
+} // namespace Texture
+} // namespace Pica
diff --git a/src/video_core/texture/etc1.h b/src/video_core/texture/etc1.h
new file mode 100644
index 000000000..e188b19df
--- /dev/null
+++ b/src/video_core/texture/etc1.h
@@ -0,0 +1,16 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "common/vector_math.h"
+
+namespace Pica {
+namespace Texture {
+
+Math::Vec3<u8> SampleETC1Subtile(u64 value, unsigned int x, unsigned int y);
+
+} // namespace Texture
+} // namespace Pica
diff --git a/src/video_core/texture/texture_decode.cpp b/src/video_core/texture/texture_decode.cpp
new file mode 100644
index 000000000..0818d652c
--- /dev/null
+++ b/src/video_core/texture/texture_decode.cpp
@@ -0,0 +1,227 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "common/color.h"
+#include "common/logging/log.h"
+#include "common/math_util.h"
+#include "common/swap.h"
+#include "common/vector_math.h"
+#include "video_core/regs_texturing.h"
+#include "video_core/texture/etc1.h"
+#include "video_core/texture/texture_decode.h"
+#include "video_core/utils.h"
+
+using TextureFormat = Pica::TexturingRegs::TextureFormat;
+
+namespace Pica {
+namespace Texture {
+
+constexpr size_t TILE_SIZE = 8 * 8;
+constexpr size_t ETC1_SUBTILES = 2 * 2;
+
+size_t CalculateTileSize(TextureFormat format) {
+ switch (format) {
+ case TextureFormat::RGBA8:
+ return 4 * TILE_SIZE;
+
+ case TextureFormat::RGB8:
+ return 3 * TILE_SIZE;
+
+ case TextureFormat::RGB5A1:
+ case TextureFormat::RGB565:
+ case TextureFormat::RGBA4:
+ case TextureFormat::IA8:
+ case TextureFormat::RG8:
+ return 2 * TILE_SIZE;
+
+ case TextureFormat::I8:
+ case TextureFormat::A8:
+ case TextureFormat::IA4:
+ return 1 * TILE_SIZE;
+
+ case TextureFormat::I4:
+ case TextureFormat::A4:
+ return TILE_SIZE / 2;
+
+ case TextureFormat::ETC1:
+ return ETC1_SUBTILES * 8;
+
+ case TextureFormat::ETC1A4:
+ return ETC1_SUBTILES * 16;
+
+ default: // placeholder for yet unknown formats
+ UNIMPLEMENTED();
+ return 0;
+ }
+}
+
+Math::Vec4<u8> LookupTexture(const u8* source, unsigned int x, unsigned int y,
+ const TextureInfo& info, bool disable_alpha) {
+ // Coordinate in tiles
+ const unsigned int coarse_x = x / 8;
+ const unsigned int coarse_y = y / 8;
+
+ // Coordinate inside the tile
+ const unsigned int fine_x = x % 8;
+ const unsigned int fine_y = y % 8;
+
+ const u8* line = source + coarse_y * info.stride;
+ const u8* tile = line + coarse_x * CalculateTileSize(info.format);
+ return LookupTexelInTile(tile, fine_x, fine_y, info, disable_alpha);
+}
+
+Math::Vec4<u8> LookupTexelInTile(const u8* source, unsigned int x, unsigned int y,
+ const TextureInfo& info, bool disable_alpha) {
+ DEBUG_ASSERT(x < 8);
+ DEBUG_ASSERT(y < 8);
+
+ using VideoCore::MortonInterleave;
+
+ switch (info.format) {
+ case TextureFormat::RGBA8: {
+ auto res = Color::DecodeRGBA8(source + MortonInterleave(x, y) * 4);
+ return {res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a())};
+ }
+
+ case TextureFormat::RGB8: {
+ auto res = Color::DecodeRGB8(source + MortonInterleave(x, y) * 3);
+ return {res.r(), res.g(), res.b(), 255};
+ }
+
+ case TextureFormat::RGB5A1: {
+ auto res = Color::DecodeRGB5A1(source + MortonInterleave(x, y) * 2);
+ return {res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a())};
+ }
+
+ case TextureFormat::RGB565: {
+ auto res = Color::DecodeRGB565(source + MortonInterleave(x, y) * 2);
+ return {res.r(), res.g(), res.b(), 255};
+ }
+
+ case TextureFormat::RGBA4: {
+ auto res = Color::DecodeRGBA4(source + MortonInterleave(x, y) * 2);
+ return {res.r(), res.g(), res.b(), static_cast<u8>(disable_alpha ? 255 : res.a())};
+ }
+
+ case TextureFormat::IA8: {
+ const u8* source_ptr = source + MortonInterleave(x, y) * 2;
+
+ if (disable_alpha) {
+ // Show intensity as red, alpha as green
+ return {source_ptr[1], source_ptr[0], 0, 255};
+ } else {
+ return {source_ptr[1], source_ptr[1], source_ptr[1], source_ptr[0]};
+ }
+ }
+
+ case TextureFormat::RG8: {
+ auto res = Color::DecodeRG8(source + MortonInterleave(x, y) * 2);
+ return {res.r(), res.g(), 0, 255};
+ }
+
+ case TextureFormat::I8: {
+ const u8* source_ptr = source + MortonInterleave(x, y);
+ return {*source_ptr, *source_ptr, *source_ptr, 255};
+ }
+
+ case TextureFormat::A8: {
+ const u8* source_ptr = source + MortonInterleave(x, y);
+
+ if (disable_alpha) {
+ return {*source_ptr, *source_ptr, *source_ptr, 255};
+ } else {
+ return {0, 0, 0, *source_ptr};
+ }
+ }
+
+ case TextureFormat::IA4: {
+ const u8* source_ptr = source + MortonInterleave(x, y);
+
+ u8 i = Color::Convert4To8(((*source_ptr) & 0xF0) >> 4);
+ u8 a = Color::Convert4To8((*source_ptr) & 0xF);
+
+ if (disable_alpha) {
+ // Show intensity as red, alpha as green
+ return {i, a, 0, 255};
+ } else {
+ return {i, i, i, a};
+ }
+ }
+
+ case TextureFormat::I4: {
+ u32 morton_offset = MortonInterleave(x, y);
+ const u8* source_ptr = source + morton_offset / 2;
+
+ u8 i = (morton_offset % 2) ? ((*source_ptr & 0xF0) >> 4) : (*source_ptr & 0xF);
+ i = Color::Convert4To8(i);
+
+ return {i, i, i, 255};
+ }
+
+ case TextureFormat::A4: {
+ u32 morton_offset = MortonInterleave(x, y);
+ const u8* source_ptr = source + morton_offset / 2;
+
+ u8 a = (morton_offset % 2) ? ((*source_ptr & 0xF0) >> 4) : (*source_ptr & 0xF);
+ a = Color::Convert4To8(a);
+
+ if (disable_alpha) {
+ return {a, a, a, 255};
+ } else {
+ return {0, 0, 0, a};
+ }
+ }
+
+ case TextureFormat::ETC1:
+ case TextureFormat::ETC1A4: {
+ bool has_alpha = (info.format == TextureFormat::ETC1A4);
+ size_t subtile_size = has_alpha ? 16 : 8;
+
+ // ETC1 further subdivides each 8x8 tile into four 4x4 subtiles
+ constexpr unsigned int subtile_width = 4;
+ constexpr unsigned int subtile_height = 4;
+
+ unsigned int subtile_index = (x / subtile_width) + 2 * (y / subtile_height);
+ x %= subtile_width;
+ y %= subtile_height;
+
+ const u8* subtile_ptr = source + subtile_index * subtile_size;
+
+ u8 alpha = 255;
+ if (has_alpha) {
+ u64_le packed_alpha;
+ memcpy(&packed_alpha, subtile_ptr, sizeof(u64));
+ subtile_ptr += sizeof(u64);
+
+ alpha = Color::Convert4To8((packed_alpha >> (4 * (x * subtile_width + y))) & 0xF);
+ }
+
+ u64_le subtile_data;
+ memcpy(&subtile_data, subtile_ptr, sizeof(u64));
+
+ return Math::MakeVec(SampleETC1Subtile(subtile_data, x, y),
+ disable_alpha ? (u8)255 : alpha);
+ }
+
+ default:
+ LOG_ERROR(HW_GPU, "Unknown texture format: %x", (u32)info.format);
+ DEBUG_ASSERT(false);
+ return {};
+ }
+}
+
+TextureInfo TextureInfo::FromPicaRegister(const TexturingRegs::TextureConfig& config,
+ const TexturingRegs::TextureFormat& format) {
+ TextureInfo info;
+ info.physical_address = config.GetPhysicalAddress();
+ info.width = config.width;
+ info.height = config.height;
+ info.format = format;
+ info.SetDefaultStride();
+ return info;
+}
+
+} // namespace Texture
+} // namespace Pica
diff --git a/src/video_core/texture/texture_decode.h b/src/video_core/texture/texture_decode.h
new file mode 100644
index 000000000..8507cfeb8
--- /dev/null
+++ b/src/video_core/texture/texture_decode.h
@@ -0,0 +1,60 @@
+// Copyright 2017 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "common/vector_math.h"
+#include "video_core/regs_texturing.h"
+
+namespace Pica {
+namespace Texture {
+
+/// Returns the byte size of a 8*8 tile of the specified texture format.
+size_t CalculateTileSize(TexturingRegs::TextureFormat format);
+
+struct TextureInfo {
+ PAddr physical_address;
+ unsigned int width;
+ unsigned int height;
+ ptrdiff_t stride;
+ TexturingRegs::TextureFormat format;
+
+ static TextureInfo FromPicaRegister(const TexturingRegs::TextureConfig& config,
+ const TexturingRegs::TextureFormat& format);
+
+ /// Calculates stride from format and width, assuming that the entire texture is contiguous.
+ void SetDefaultStride() {
+ stride = CalculateTileSize(format) * (width / 8);
+ }
+};
+
+/**
+ * Lookup texel located at the given coordinates and return an RGBA vector of its color.
+ * @param source Source pointer to read data from
+ * @param x,y Texture coordinates to read from
+ * @param info TextureInfo object describing the texture setup
+ * @param disable_alpha This is used for debug widgets which use this method to display textures
+ * without providing a good way to visualize alpha by themselves. If true, this will return 255 for
+ * the alpha component, and either drop the information entirely or store it in an "unused" color
+ * channel.
+ * @todo Eventually we should get rid of the disable_alpha parameter.
+ */
+Math::Vec4<u8> LookupTexture(const u8* source, unsigned int x, unsigned int y,
+ const TextureInfo& info, bool disable_alpha = false);
+
+/**
+ * Looks up a texel from a single 8x8 texture tile.
+ *
+ * @param source Pointer to the beginning of the tile.
+ * @param x, y In-tile coordinates to read from. Must be < 8.
+ * @param info TextureInfo describing the texture format.
+ * @param disable_alpha Used for debugging. Sets the result alpha to 255 and either discards the
+ * real alpha or inserts it in an otherwise unused channel.
+ */
+Math::Vec4<u8> LookupTexelInTile(const u8* source, unsigned int x, unsigned int y,
+ const TextureInfo& info, bool disable_alpha);
+
+} // namespace Texture
+} // namespace Pica
diff --git a/src/video_core/vertex_loader.cpp b/src/video_core/vertex_loader.cpp
index 2b8ef7018..37c5224a9 100644
--- a/src/video_core/vertex_loader.cpp
+++ b/src/video_core/vertex_loader.cpp
@@ -8,15 +8,15 @@
#include "common/vector_math.h"
#include "core/memory.h"
#include "video_core/debug_utils/debug_utils.h"
-#include "video_core/pica.h"
#include "video_core/pica_state.h"
#include "video_core/pica_types.h"
+#include "video_core/regs_pipeline.h"
#include "video_core/shader/shader.h"
#include "video_core/vertex_loader.h"
namespace Pica {
-void VertexLoader::Setup(const Pica::Regs& regs) {
+void VertexLoader::Setup(const PipelineRegs& regs) {
ASSERT_MSG(!is_setup, "VertexLoader is not intended to be setup more than once.");
const auto& attribute_config = regs.vertex_attributes;
@@ -70,7 +70,8 @@ void VertexLoader::Setup(const Pica::Regs& regs) {
is_setup = true;
}
-void VertexLoader::LoadVertex(u32 base_address, int index, int vertex, Shader::InputVertex& input,
+void VertexLoader::LoadVertex(u32 base_address, int index, int vertex,
+ Shader::AttributeBuffer& input,
DebugUtils::MemoryAccessTracker& memory_accesses) {
ASSERT_MSG(is_setup, "A VertexLoader needs to be setup before loading vertices.");
@@ -84,15 +85,16 @@ void VertexLoader::LoadVertex(u32 base_address, int index, int vertex, Shader::I
memory_accesses.AddAccess(
source_addr,
vertex_attribute_elements[i] *
- ((vertex_attribute_formats[i] == Regs::VertexAttributeFormat::FLOAT)
+ ((vertex_attribute_formats[i] == PipelineRegs::VertexAttributeFormat::FLOAT)
? 4
- : (vertex_attribute_formats[i] == Regs::VertexAttributeFormat::SHORT)
+ : (vertex_attribute_formats[i] ==
+ PipelineRegs::VertexAttributeFormat::SHORT)
? 2
: 1));
}
switch (vertex_attribute_formats[i]) {
- case Regs::VertexAttributeFormat::BYTE: {
+ case PipelineRegs::VertexAttributeFormat::BYTE: {
const s8* srcdata =
reinterpret_cast<const s8*>(Memory::GetPhysicalPointer(source_addr));
for (unsigned int comp = 0; comp < vertex_attribute_elements[i]; ++comp) {
@@ -100,7 +102,7 @@ void VertexLoader::LoadVertex(u32 base_address, int index, int vertex, Shader::I
}
break;
}
- case Regs::VertexAttributeFormat::UBYTE: {
+ case PipelineRegs::VertexAttributeFormat::UBYTE: {
const u8* srcdata =
reinterpret_cast<const u8*>(Memory::GetPhysicalPointer(source_addr));
for (unsigned int comp = 0; comp < vertex_attribute_elements[i]; ++comp) {
@@ -108,7 +110,7 @@ void VertexLoader::LoadVertex(u32 base_address, int index, int vertex, Shader::I
}
break;
}
- case Regs::VertexAttributeFormat::SHORT: {
+ case PipelineRegs::VertexAttributeFormat::SHORT: {
const s16* srcdata =
reinterpret_cast<const s16*>(Memory::GetPhysicalPointer(source_addr));
for (unsigned int comp = 0; comp < vertex_attribute_elements[i]; ++comp) {
@@ -116,7 +118,7 @@ void VertexLoader::LoadVertex(u32 base_address, int index, int vertex, Shader::I
}
break;
}
- case Regs::VertexAttributeFormat::FLOAT: {
+ case PipelineRegs::VertexAttributeFormat::FLOAT: {
const float* srcdata =
reinterpret_cast<const float*>(Memory::GetPhysicalPointer(source_addr));
for (unsigned int comp = 0; comp < vertex_attribute_elements[i]; ++comp) {
@@ -142,7 +144,7 @@ void VertexLoader::LoadVertex(u32 base_address, int index, int vertex, Shader::I
input.attr[i][2].ToFloat32(), input.attr[i][3].ToFloat32());
} else if (vertex_attribute_is_default[i]) {
// Load the default attribute if we're configured to do so
- input.attr[i] = g_state.vs_default_attributes[i];
+ input.attr[i] = g_state.input_default_attributes.attr[i];
LOG_TRACE(HW_GPU,
"Loaded default attribute %x for vertex %x (index %x): (%f, %f, %f, %f)", i,
vertex, index, input.attr[i][0].ToFloat32(), input.attr[i][1].ToFloat32(),
diff --git a/src/video_core/vertex_loader.h b/src/video_core/vertex_loader.h
index 9f2098bb2..02db10aee 100644
--- a/src/video_core/vertex_loader.h
+++ b/src/video_core/vertex_loader.h
@@ -2,7 +2,7 @@
#include <array>
#include "common/common_types.h"
-#include "video_core/pica.h"
+#include "video_core/regs_pipeline.h"
namespace Pica {
@@ -11,18 +11,18 @@ class MemoryAccessTracker;
}
namespace Shader {
-struct InputVertex;
+struct AttributeBuffer;
}
class VertexLoader {
public:
VertexLoader() = default;
- explicit VertexLoader(const Pica::Regs& regs) {
+ explicit VertexLoader(const PipelineRegs& regs) {
Setup(regs);
}
- void Setup(const Pica::Regs& regs);
- void LoadVertex(u32 base_address, int index, int vertex, Shader::InputVertex& input,
+ void Setup(const PipelineRegs& regs);
+ void LoadVertex(u32 base_address, int index, int vertex, Shader::AttributeBuffer& input,
DebugUtils::MemoryAccessTracker& memory_accesses);
int GetNumTotalAttributes() const {
@@ -32,7 +32,7 @@ public:
private:
std::array<u32, 16> vertex_attribute_sources;
std::array<u32, 16> vertex_attribute_strides{};
- std::array<Regs::VertexAttributeFormat, 16> vertex_attribute_formats;
+ std::array<PipelineRegs::VertexAttributeFormat, 16> vertex_attribute_formats;
std::array<u32, 16> vertex_attribute_elements{};
std::array<bool, 16> vertex_attribute_is_default;
int num_total_attributes = 0;