diff options
Diffstat (limited to 'src/video_core')
19 files changed, 625 insertions, 649 deletions
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 581a37897..6ca319b59 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -39,6 +39,7 @@ set(HEADERS rasterizer.h rasterizer_interface.h renderer_base.h + shader/debug_data.h shader/shader.h shader/shader_interpreter.h swrasterizer.h @@ -59,6 +60,9 @@ create_directory_groups(${SRCS} ${HEADERS}) add_library(video_core STATIC ${SRCS} ${HEADERS}) target_link_libraries(video_core glad) +if (ARCHITECTURE_x86_64) + target_link_libraries(video_core xbyak) +endif() if (PNG_FOUND) target_link_libraries(video_core ${PNG_LIBRARIES}) diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index c80c96762..ea58e9f54 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp @@ -59,7 +59,10 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { regs[id] = (old_value & ~write_mask) | (value & write_mask); - DebugUtils::OnPicaRegWrite({(u16)id, (u16)mask, regs[id]}); + // Double check for is_pica_tracing to avoid call overhead + if (DebugUtils::IsPicaTracing()) { + DebugUtils::OnPicaRegWrite({(u16)id, (u16)mask, regs[id]}); + } if (g_debug_context) g_debug_context->OnEvent(DebugContext::Event::PicaCommandLoaded, @@ -68,7 +71,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { switch (id) { // Trigger IRQ case PICA_REG_INDEX(trigger_irq): - GSP_GPU::SignalInterrupt(GSP_GPU::InterruptId::P3D); + Service::GSP::SignalInterrupt(Service::GSP::InterruptId::P3D); break; case PICA_REG_INDEX_WORKAROUND(triangle_topology, 0x25E): @@ -136,9 +139,10 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { immediate_input.attr[immediate_attribute_id++] = attribute; if (immediate_attribute_id >= regs.vs.num_input_attributes + 1) { + MICROPROFILE_SCOPE(GPU_Drawing); immediate_attribute_id = 0; - Shader::UnitState<false> shader_unit; + Shader::UnitState shader_unit; g_state.vs.Setup(); // Send to vertex shader @@ -165,6 +169,8 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { case PICA_REG_INDEX(gpu_mode): if (regs.gpu_mode == Regs::GPUMode::Configuring) { + MICROPROFILE_SCOPE(GPU_Drawing); + // Draw immediate mode triangles when GPU Mode is set to GPUMode::Configuring VideoCore::g_renderer->Rasterizer()->DrawTriangles(); @@ -237,7 +243,7 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { unsigned int vertex_cache_pos = 0; vertex_cache_ids.fill(-1); - Shader::UnitState<false> shader_unit; + Shader::UnitState shader_unit; g_state.vs.Setup(); for (unsigned int index = 0; index < regs.num_vertices; ++index) { @@ -251,7 +257,6 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { ASSERT(vertex != -1); bool vertex_cache_hit = false; - Shader::OutputRegisters output_registers; if (is_indexed) { if (g_debug_context && Pica::g_debug_context->recorder) { @@ -279,10 +284,9 @@ static void WritePicaReg(u32 id, u32 value, u32 mask) { g_debug_context->OnEvent(DebugContext::Event::VertexShaderInvocation, (void*)&input); g_state.vs.Run(shader_unit, input, loader.GetNumTotalAttributes()); - output_registers = shader_unit.output_registers; // Retrieve vertex from register data - output_vertex = output_registers.ToVertex(regs.vs); + output_vertex = shader_unit.output_registers.ToVertex(regs.vs); if (is_indexed) { vertex_cache[vertex_cache_pos] = output_vertex; diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp index 8806464d9..c44b3d95a 100644 --- a/src/video_core/debug_utils/debug_utils.cpp +++ b/src/video_core/debug_utils/debug_utils.cpp @@ -276,10 +276,10 @@ void DumpShader(const std::string& filename, const Regs::ShaderConfig& config, static std::unique_ptr<PicaTrace> pica_trace; static std::mutex pica_trace_mutex; -static int is_pica_tracing = false; +bool g_is_pica_tracing = false; void StartPicaTracing() { - if (is_pica_tracing) { + if (g_is_pica_tracing) { LOG_WARNING(HW_GPU, "StartPicaTracing called even though tracing already running!"); return; } @@ -287,34 +287,26 @@ void StartPicaTracing() { std::lock_guard<std::mutex> lock(pica_trace_mutex); pica_trace = std::make_unique<PicaTrace>(); - is_pica_tracing = true; -} - -bool IsPicaTracing() { - return is_pica_tracing != 0; + g_is_pica_tracing = true; } void OnPicaRegWrite(PicaTrace::Write write) { - // Double check for is_pica_tracing to avoid pointless locking overhead - if (!is_pica_tracing) - return; - std::lock_guard<std::mutex> lock(pica_trace_mutex); - if (!is_pica_tracing) + if (!g_is_pica_tracing) return; pica_trace->writes.push_back(write); } std::unique_ptr<PicaTrace> FinishPicaTracing() { - if (!is_pica_tracing) { + if (!g_is_pica_tracing) { LOG_WARNING(HW_GPU, "FinishPicaTracing called even though tracing isn't running!"); return {}; } // signalize that no further tracing should be performed - is_pica_tracing = false; + g_is_pica_tracing = false; // Wait until running tracing is finished std::lock_guard<std::mutex> lock(pica_trace_mutex); diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h index 189c93abb..46ea8d9c7 100644 --- a/src/video_core/debug_utils/debug_utils.h +++ b/src/video_core/debug_utils/debug_utils.h @@ -196,8 +196,12 @@ struct PicaTrace { std::vector<Write> writes; }; +extern bool g_is_pica_tracing; + void StartPicaTracing(); -bool IsPicaTracing(); +inline bool IsPicaTracing() { + return g_is_pica_tracing; +} void OnPicaRegWrite(PicaTrace::Write write); std::unique_ptr<PicaTrace> FinishPicaTracing(); diff --git a/src/video_core/gpu_debugger.h b/src/video_core/gpu_debugger.h index 3c6636d66..c1f9b43c2 100644 --- a/src/video_core/gpu_debugger.h +++ b/src/video_core/gpu_debugger.h @@ -28,7 +28,8 @@ public: * @note All methods in this class are called from the GSP thread */ virtual void GXCommandProcessed(int total_command_count) { - const GSP_GPU::Command& cmd = observed->ReadGXCommandHistory(total_command_count - 1); + const Service::GSP::Command& cmd = + observed->ReadGXCommandHistory(total_command_count - 1); LOG_TRACE(Debug_GPU, "Received command: id=%x", (int)cmd.id.Value()); } @@ -48,16 +49,16 @@ public: return; gx_command_history.emplace_back(); - GSP_GPU::Command& cmd = gx_command_history.back(); + Service::GSP::Command& cmd = gx_command_history.back(); - memcpy(&cmd, command_data, sizeof(GSP_GPU::Command)); + memcpy(&cmd, command_data, sizeof(Service::GSP::Command)); ForEachObserver([this](DebuggerObserver* observer) { observer->GXCommandProcessed(static_cast<int>(this->gx_command_history.size())); }); } - const GSP_GPU::Command& ReadGXCommandHistory(int index) const { + const Service::GSP::Command& ReadGXCommandHistory(int index) const { // TODO: Is this thread-safe? return gx_command_history[index]; } @@ -80,5 +81,5 @@ private: std::vector<DebuggerObserver*> observers; - std::vector<GSP_GPU::Command> gx_command_history; + std::vector<Service::GSP::Command> gx_command_history; }; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index ba65db419..5a306a5c8 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -11,6 +11,7 @@ #include "common/color.h" #include "common/logging/log.h" #include "common/math_util.h" +#include "common/microprofile.h" #include "common/vector_math.h" #include "core/hw/gpu.h" #include "video_core/pica.h" @@ -21,6 +22,10 @@ #include "video_core/renderer_opengl/pica_to_gl.h" #include "video_core/renderer_opengl/renderer_opengl.h" +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 && @@ -168,6 +173,7 @@ void RasterizerOpenGL::DrawTriangles() { if (vertex_batch.empty()) return; + MICROPROFILE_SCOPE(OpenGL_Drawing); const auto& regs = Pica::g_state.regs; // Sync and bind the framebuffer surfaces @@ -189,10 +195,6 @@ void RasterizerOpenGL::DrawTriangles() { GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, (has_stencil && depth_surface != nullptr) ? depth_surface->texture.handle : 0, 0); - if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - return; - } - // 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; @@ -698,18 +700,22 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { } void RasterizerOpenGL::FlushAll() { + MICROPROFILE_SCOPE(OpenGL_CacheManagement); res_cache.FlushAll(); } void RasterizerOpenGL::FlushRegion(PAddr addr, u32 size) { + MICROPROFILE_SCOPE(OpenGL_CacheManagement); res_cache.FlushRegion(addr, size, nullptr, false); } void RasterizerOpenGL::FlushAndInvalidateRegion(PAddr addr, u32 size) { + MICROPROFILE_SCOPE(OpenGL_CacheManagement); res_cache.FlushRegion(addr, size, nullptr, true); } bool RasterizerOpenGL::AccelerateDisplayTransfer(const GPU::Regs::DisplayTransferConfig& config) { + MICROPROFILE_SCOPE(OpenGL_Blits); using PixelFormat = CachedSurface::PixelFormat; using SurfaceType = CachedSurface::SurfaceType; @@ -782,6 +788,7 @@ bool RasterizerOpenGL::AccelerateTextureCopy(const GPU::Regs::DisplayTransferCon } bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config) { + MICROPROFILE_SCOPE(OpenGL_Blits); using PixelFormat = CachedSurface::PixelFormat; using SurfaceType = CachedSurface::SurfaceType; @@ -807,10 +814,6 @@ bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config) glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); - if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - return false; - } - GLfloat color_values[4] = {0.0f, 0.0f, 0.0f, 0.0f}; // TODO: Handle additional pixel format and fill value size combinations to accelerate more @@ -883,10 +886,10 @@ bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config) } } - cur_state.color_mask.red_enabled = true; - cur_state.color_mask.green_enabled = true; - cur_state.color_mask.blue_enabled = true; - cur_state.color_mask.alpha_enabled = true; + cur_state.color_mask.red_enabled = GL_TRUE; + cur_state.color_mask.green_enabled = GL_TRUE; + cur_state.color_mask.blue_enabled = GL_TRUE; + cur_state.color_mask.alpha_enabled = GL_TRUE; cur_state.Apply(); glClearBufferfv(GL_COLOR, 0, color_values); } else if (dst_type == SurfaceType::Depth) { @@ -895,10 +898,6 @@ bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config) dst_surface->texture.handle, 0); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); - if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - return false; - } - GLfloat value_float; if (dst_surface->pixel_format == CachedSurface::PixelFormat::D16) { value_float = config.value_32bit / 65535.0f; // 2^16 - 1 @@ -914,10 +913,6 @@ bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config) glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, dst_surface->texture.handle, 0); - if (OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { - return false; - } - GLfloat value_float = (config.value_32bit & 0xFFFFFF) / 16777215.0f; // 2^24 - 1 GLint value_int = (config.value_32bit >> 24); @@ -942,6 +937,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const GPU::Regs::FramebufferConfig& con if (framebuffer_addr == 0) { return false; } + MICROPROFILE_SCOPE(OpenGL_CacheManagement); CachedSurface src_params; src_params.addr = framebuffer_addr; diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 61f6e767f..85aa06cd5 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -11,11 +11,11 @@ #include <vector> #include <glad/glad.h> #include "common/bit_field.h" -#include "common/emu_window.h" #include "common/logging/log.h" #include "common/math_util.h" #include "common/microprofile.h" #include "common/vector_math.h" +#include "core/frontend/emu_window.h" #include "core/memory.h" #include "video_core/debug_utils/debug_utils.h" #include "video_core/pica_state.h" @@ -103,7 +103,7 @@ static void MortonCopyPixels(CachedSurface::PixelFormat pixel_format, u32 width, } } -bool RasterizerCacheOpenGL::BlitTextures(GLuint src_tex, GLuint dst_tex, +void RasterizerCacheOpenGL::BlitTextures(GLuint src_tex, GLuint dst_tex, CachedSurface::SurfaceType type, const MathUtil::Rectangle<int>& src_rect, const MathUtil::Rectangle<int>& dst_rect) { @@ -158,21 +158,14 @@ bool RasterizerCacheOpenGL::BlitTextures(GLuint src_tex, GLuint dst_tex, buffers = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; } - bool can_blit = OpenGLState::CheckFBStatus(GL_READ_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE && - OpenGLState::CheckFBStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE; - - if (can_blit) { - glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom, - dst_rect.left, dst_rect.top, dst_rect.right, dst_rect.bottom, buffers, - buffers == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST); - } + glBlitFramebuffer(src_rect.left, src_rect.top, src_rect.right, src_rect.bottom, dst_rect.left, + dst_rect.top, dst_rect.right, dst_rect.bottom, buffers, + buffers == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST); // Restore previous framebuffer bindings cur_state.draw.read_framebuffer = old_fbs[0]; cur_state.draw.draw_framebuffer = old_fbs[1]; cur_state.Apply(); - - return can_blit; } bool RasterizerCacheOpenGL::TryBlitSurfaces(CachedSurface* src_surface, @@ -186,9 +179,9 @@ bool RasterizerCacheOpenGL::TryBlitSurfaces(CachedSurface* src_surface, return false; } - return BlitTextures(src_surface->texture.handle, dst_surface->texture.handle, - CachedSurface::GetFormatType(src_surface->pixel_format), src_rect, - dst_rect); + BlitTextures(src_surface->texture.handle, dst_surface->texture.handle, + CachedSurface::GetFormatType(src_surface->pixel_format), src_rect, dst_rect); + return true; } static void AllocateSurfaceTexture(GLuint texture, CachedSurface::PixelFormat pixel_format, diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 32abfbaf5..b50e8292b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -187,7 +187,7 @@ public: ~RasterizerCacheOpenGL(); /// Blits one texture to another - bool BlitTextures(GLuint src_tex, GLuint dst_tex, CachedSurface::SurfaceType type, + void BlitTextures(GLuint src_tex, GLuint dst_tex, CachedSurface::SurfaceType type, const MathUtil::Rectangle<int>& src_rect, const MathUtil::Rectangle<int>& dst_rect); diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 8f278722d..4c4f98ac9 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -293,7 +293,7 @@ static void AppendAlphaTestCondition(std::string& out, Regs::CompareFunc func) { case CompareFunc::GreaterThanOrEqual: { static const char* op[] = {"!=", "==", ">=", ">", "<=", "<"}; unsigned index = (unsigned)func - (unsigned)CompareFunc::Equal; - out += "int(last_tex_env_out.a * 255.0f) " + std::string(op[index]) + " alphatest_ref"; + out += "int(last_tex_env_out.a * 255.0) " + std::string(op[index]) + " alphatest_ref"; break; } @@ -422,16 +422,13 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { if (abs) { // LUT index is in the range of (0.0, 1.0) index = lighting.light[light_num].two_sided_diffuse ? "abs(" + index + ")" - : "max(" + index + ", 0.f)"; - return "(FLOAT_255 * clamp(" + index + ", 0.0, 1.0))"; + : "max(" + index + ", 0.0)"; } else { // LUT index is in the range of (-1.0, 1.0) - index = "clamp(" + index + ", -1.0, 1.0)"; - return "(FLOAT_255 * ((" + index + " < 0) ? " + index + " + 2.0 : " + index + - ") / 2.0)"; + index = "((" + index + " < 0) ? " + index + " + 2.0 : " + index + ") / 2.0"; } - return std::string(); + return "(OFFSET_256 + SCALE_256 * clamp(" + index + ", 0.0, 1.0))"; }; // Gets the lighting lookup table value given the specified sampler and index @@ -462,7 +459,7 @@ static void WriteLighting(std::string& out, const PicaShaderConfig& config) { if (light_config.dist_atten_enable) { std::string index = "(" + light_src + ".dist_atten_scale * length(-view - " + light_src + ".position) + " + light_src + ".dist_atten_bias)"; - index = "((clamp(" + index + ", 0.0, FLOAT_255)))"; + 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); @@ -580,8 +577,10 @@ std::string GenerateFragmentShader(const PicaShaderConfig& config) { #version 330 core #define NUM_TEV_STAGES 6 #define NUM_LIGHTS 8 -#define LIGHTING_LUT_SIZE 256 -#define FLOAT_255 (255.0 / 256.0) + +// Texture coordinate offsets and scales +#define OFFSET_256 (0.5 / 256.0) +#define SCALE_256 (255.0 / 256.0) in vec4 primary_color; in vec2 texcoord[3]; diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 2a731f483..3c03b424a 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -232,19 +232,6 @@ void OpenGLState::Apply() const { cur_state = *this; } -GLenum OpenGLState::CheckFBStatus(GLenum target) { - GLenum fb_status = glCheckFramebufferStatus(target); - if (fb_status != GL_FRAMEBUFFER_COMPLETE) { - const char* fb_description = - (target == GL_READ_FRAMEBUFFER ? "READ" - : (target == GL_DRAW_FRAMEBUFFER ? "DRAW" : "UNK")); - LOG_CRITICAL(Render_OpenGL, "OpenGL %s framebuffer check failed, status %X", fb_description, - fb_status); - } - - return fb_status; -} - void OpenGLState::ResetTexture(GLuint handle) { for (auto& unit : cur_state.texture_units) { if (unit.texture_2d == handle) { diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 01dead883..aee3c2946 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -90,9 +90,6 @@ public: /// Apply this state as the current OpenGL state void Apply() const; - /// Check the status of the current OpenGL read or draw framebuffer configuration - static GLenum CheckFBStatus(GLenum target); - /// Resets and unbinds any references to the given resource in the current OpenGL state static void ResetTexture(GLuint handle); static void ResetSampler(GLuint handle); diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index 93f0ac105..2aa90e5c1 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -9,10 +9,10 @@ #include <glad/glad.h> #include "common/assert.h" #include "common/bit_field.h" -#include "common/emu_window.h" #include "common/logging/log.h" #include "common/profiler_reporting.h" #include "common/synchronized_wrapper.h" +#include "core/frontend/emu_window.h" #include "core/hw/gpu.h" #include "core/hw/hw.h" #include "core/hw/lcd.h" diff --git a/src/video_core/shader/debug_data.h b/src/video_core/shader/debug_data.h new file mode 100644 index 000000000..9e82122e1 --- /dev/null +++ b/src/video_core/shader/debug_data.h @@ -0,0 +1,186 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <vector> +#include "common/common_types.h" +#include "common/vector_math.h" +#include "video_core/pica_types.h" + +namespace Pica { +namespace Shader { + +/// Helper structure used to keep track of data useful for inspection of shader emulation +template <bool full_debugging> +struct DebugData; + +template <> +struct DebugData<false> { + // TODO: Hide these behind and interface and move them to DebugData<true> + u32 max_offset = 0; ///< maximum program counter ever reached + u32 max_opdesc_id = 0; ///< maximum swizzle pattern index ever used +}; + +template <> +struct DebugData<true> { + /// Records store the input and output operands of a particular instruction. + struct Record { + enum Type { + // Floating point arithmetic operands + SRC1 = 0x1, + SRC2 = 0x2, + SRC3 = 0x4, + + // Initial and final output operand value + DEST_IN = 0x8, + DEST_OUT = 0x10, + + // Current and next instruction offset (in words) + CUR_INSTR = 0x20, + NEXT_INSTR = 0x40, + + // Output address register value + ADDR_REG_OUT = 0x80, + + // Result of a comparison instruction + CMP_RESULT = 0x100, + + // Input values for conditional flow control instructions + COND_BOOL_IN = 0x200, + COND_CMP_IN = 0x400, + + // Input values for a loop + LOOP_INT_IN = 0x800, + }; + + Math::Vec4<float24> src1; + Math::Vec4<float24> src2; + Math::Vec4<float24> src3; + + Math::Vec4<float24> dest_in; + Math::Vec4<float24> dest_out; + + s32 address_registers[2]; + bool conditional_code[2]; + bool cond_bool; + bool cond_cmp[2]; + Math::Vec4<u8> loop_int; + + u32 instruction_offset; + u32 next_instruction; + + /// set of enabled fields (as a combination of Type flags) + unsigned mask = 0; + }; + + u32 max_offset = 0; ///< maximum program counter ever reached + u32 max_opdesc_id = 0; ///< maximum swizzle pattern index ever used + + /// List of records for each executed shader instruction + std::vector<DebugData<true>::Record> records; +}; + +/// Type alias for better readability +using DebugDataRecord = DebugData<true>::Record; + +/// Helper function to set a DebugData<true>::Record field based on the template enum parameter. +template <DebugDataRecord::Type type, typename ValueType> +inline void SetField(DebugDataRecord& record, ValueType value); + +template <> +inline void SetField<DebugDataRecord::SRC1>(DebugDataRecord& record, float24* value) { + record.src1.x = value[0]; + record.src1.y = value[1]; + record.src1.z = value[2]; + record.src1.w = value[3]; +} + +template <> +inline void SetField<DebugDataRecord::SRC2>(DebugDataRecord& record, float24* value) { + record.src2.x = value[0]; + record.src2.y = value[1]; + record.src2.z = value[2]; + record.src2.w = value[3]; +} + +template <> +inline void SetField<DebugDataRecord::SRC3>(DebugDataRecord& record, float24* value) { + record.src3.x = value[0]; + record.src3.y = value[1]; + record.src3.z = value[2]; + record.src3.w = value[3]; +} + +template <> +inline void SetField<DebugDataRecord::DEST_IN>(DebugDataRecord& record, float24* value) { + record.dest_in.x = value[0]; + record.dest_in.y = value[1]; + record.dest_in.z = value[2]; + record.dest_in.w = value[3]; +} + +template <> +inline void SetField<DebugDataRecord::DEST_OUT>(DebugDataRecord& record, float24* value) { + record.dest_out.x = value[0]; + record.dest_out.y = value[1]; + record.dest_out.z = value[2]; + record.dest_out.w = value[3]; +} + +template <> +inline void SetField<DebugDataRecord::ADDR_REG_OUT>(DebugDataRecord& record, s32* value) { + record.address_registers[0] = value[0]; + record.address_registers[1] = value[1]; +} + +template <> +inline void SetField<DebugDataRecord::CMP_RESULT>(DebugDataRecord& record, bool* value) { + record.conditional_code[0] = value[0]; + record.conditional_code[1] = value[1]; +} + +template <> +inline void SetField<DebugDataRecord::COND_BOOL_IN>(DebugDataRecord& record, bool value) { + record.cond_bool = value; +} + +template <> +inline void SetField<DebugDataRecord::COND_CMP_IN>(DebugDataRecord& record, bool* value) { + record.cond_cmp[0] = value[0]; + record.cond_cmp[1] = value[1]; +} + +template <> +inline void SetField<DebugDataRecord::LOOP_INT_IN>(DebugDataRecord& record, Math::Vec4<u8> value) { + record.loop_int = value; +} + +template <> +inline void SetField<DebugDataRecord::CUR_INSTR>(DebugDataRecord& record, u32 value) { + record.instruction_offset = value; +} + +template <> +inline void SetField<DebugDataRecord::NEXT_INSTR>(DebugDataRecord& record, u32 value) { + record.next_instruction = value; +} + +/// Helper function to set debug information on the current shader iteration. +template <DebugDataRecord::Type type, typename ValueType> +inline void Record(DebugData<false>& debug_data, u32 offset, ValueType value) { + // Debugging disabled => nothing to do +} + +template <DebugDataRecord::Type type, typename ValueType> +inline void Record(DebugData<true>& debug_data, u32 offset, ValueType value) { + if (offset >= debug_data.records.size()) + debug_data.records.resize(offset + 1); + + SetField<type, ValueType>(debug_data.records[offset], value); + debug_data.records[offset].mask |= type; +} + +} // namespace Shader +} // namespace Pica diff --git a/src/video_core/shader/shader.cpp b/src/video_core/shader/shader.cpp index 3febe739c..a4aa3c9e0 100644 --- a/src/video_core/shader/shader.cpp +++ b/src/video_core/shader/shader.cpp @@ -25,7 +25,7 @@ namespace Pica { namespace Shader { -OutputVertex OutputRegisters::ToVertex(const Regs::ShaderConfig& config) { +OutputVertex OutputRegisters::ToVertex(const Regs::ShaderConfig& config) const { // Setup output data OutputVertex ret; // TODO(neobrain): Under some circumstances, up to 16 attributes may be output. We need to @@ -109,15 +109,12 @@ void ShaderSetup::Setup() { MICROPROFILE_DEFINE(GPU_Shader, "GPU", "Shader", MP_RGB(50, 50, 240)); -void ShaderSetup::Run(UnitState<false>& state, const InputVertex& input, int num_attributes) { +void ShaderSetup::Run(UnitState& state, const InputVertex& input, int num_attributes) { auto& config = g_state.regs.vs; auto& setup = g_state.vs; MICROPROFILE_SCOPE(GPU_Shader); - state.debug.max_offset = 0; - state.debug.max_opdesc_id = 0; - // Setup input register table const auto& attribute_register_map = config.input_register_map; @@ -128,22 +125,23 @@ void ShaderSetup::Run(UnitState<false>& state, const InputVertex& input, int num state.conditional_code[1] = false; #ifdef ARCHITECTURE_x86_64 - if (VideoCore::g_shader_jit_enabled) + if (VideoCore::g_shader_jit_enabled) { jit_shader->Run(setup, state, config.main_offset); - else - RunInterpreter(setup, state, config.main_offset); + } else { + DebugData<false> dummy_debug_data; + RunInterpreter(setup, state, dummy_debug_data, config.main_offset); + } #else - RunInterpreter(setup, state, config.main_offset); + DebugData<false> dummy_debug_data; + RunInterpreter(setup, state, dummy_debug_data, config.main_offset); #endif // ARCHITECTURE_x86_64 } DebugData<true> ShaderSetup::ProduceDebugInfo(const InputVertex& input, int num_attributes, const Regs::ShaderConfig& config, const ShaderSetup& setup) { - UnitState<true> state; - - state.debug.max_offset = 0; - state.debug.max_opdesc_id = 0; + UnitState state; + DebugData<true> debug_data; // Setup input register table boost::fill(state.registers.input, Math::Vec4<float24>::AssignToAll(float24::Zero())); @@ -154,8 +152,8 @@ DebugData<true> ShaderSetup::ProduceDebugInfo(const InputVertex& input, int num_ state.conditional_code[0] = false; state.conditional_code[1] = false; - RunInterpreter(setup, state, config.main_offset); - return state.debug; + RunInterpreter(setup, state, debug_data, config.main_offset); + return debug_data; } } // namespace Shader diff --git a/src/video_core/shader/shader.h b/src/video_core/shader/shader.h index 8858d67f8..2b07759b9 100644 --- a/src/video_core/shader/shader.h +++ b/src/video_core/shader/shader.h @@ -8,8 +8,6 @@ #include <cstddef> #include <memory> #include <type_traits> -#include <vector> -#include <boost/container/static_vector.hpp> #include <nihstro/shader_bytecode.h> #include "common/assert.h" #include "common/common_funcs.h" @@ -17,6 +15,7 @@ #include "common/vector_math.h" #include "video_core/pica.h" #include "video_core/pica_types.h" +#include "video_core/shader/debug_data.h" using nihstro::RegisterType; using nihstro::SourceRegister; @@ -85,187 +84,16 @@ struct OutputRegisters { alignas(16) Math::Vec4<float24> value[16]; - OutputVertex ToVertex(const Regs::ShaderConfig& config); + OutputVertex ToVertex(const Regs::ShaderConfig& config) const; }; static_assert(std::is_pod<OutputRegisters>::value, "Structure is not POD"); -// Helper structure used to keep track of data useful for inspection of shader emulation -template <bool full_debugging> -struct DebugData; - -template <> -struct DebugData<false> { - // TODO: Hide these behind and interface and move them to DebugData<true> - u32 max_offset; // maximum program counter ever reached - u32 max_opdesc_id; // maximum swizzle pattern index ever used -}; - -template <> -struct DebugData<true> { - // Records store the input and output operands of a particular instruction. - struct Record { - enum Type { - // Floating point arithmetic operands - SRC1 = 0x1, - SRC2 = 0x2, - SRC3 = 0x4, - - // Initial and final output operand value - DEST_IN = 0x8, - DEST_OUT = 0x10, - - // Current and next instruction offset (in words) - CUR_INSTR = 0x20, - NEXT_INSTR = 0x40, - - // Output address register value - ADDR_REG_OUT = 0x80, - - // Result of a comparison instruction - CMP_RESULT = 0x100, - - // Input values for conditional flow control instructions - COND_BOOL_IN = 0x200, - COND_CMP_IN = 0x400, - - // Input values for a loop - LOOP_INT_IN = 0x800, - }; - - Math::Vec4<float24> src1; - Math::Vec4<float24> src2; - Math::Vec4<float24> src3; - - Math::Vec4<float24> dest_in; - Math::Vec4<float24> dest_out; - - s32 address_registers[2]; - bool conditional_code[2]; - bool cond_bool; - bool cond_cmp[2]; - Math::Vec4<u8> loop_int; - - u32 instruction_offset; - u32 next_instruction; - - // set of enabled fields (as a combination of Type flags) - unsigned mask = 0; - }; - - u32 max_offset; // maximum program counter ever reached - u32 max_opdesc_id; // maximum swizzle pattern index ever used - - // List of records for each executed shader instruction - std::vector<DebugData<true>::Record> records; -}; - -// Type alias for better readability -using DebugDataRecord = DebugData<true>::Record; - -// Helper function to set a DebugData<true>::Record field based on the template enum parameter. -template <DebugDataRecord::Type type, typename ValueType> -inline void SetField(DebugDataRecord& record, ValueType value); - -template <> -inline void SetField<DebugDataRecord::SRC1>(DebugDataRecord& record, float24* value) { - record.src1.x = value[0]; - record.src1.y = value[1]; - record.src1.z = value[2]; - record.src1.w = value[3]; -} - -template <> -inline void SetField<DebugDataRecord::SRC2>(DebugDataRecord& record, float24* value) { - record.src2.x = value[0]; - record.src2.y = value[1]; - record.src2.z = value[2]; - record.src2.w = value[3]; -} - -template <> -inline void SetField<DebugDataRecord::SRC3>(DebugDataRecord& record, float24* value) { - record.src3.x = value[0]; - record.src3.y = value[1]; - record.src3.z = value[2]; - record.src3.w = value[3]; -} - -template <> -inline void SetField<DebugDataRecord::DEST_IN>(DebugDataRecord& record, float24* value) { - record.dest_in.x = value[0]; - record.dest_in.y = value[1]; - record.dest_in.z = value[2]; - record.dest_in.w = value[3]; -} - -template <> -inline void SetField<DebugDataRecord::DEST_OUT>(DebugDataRecord& record, float24* value) { - record.dest_out.x = value[0]; - record.dest_out.y = value[1]; - record.dest_out.z = value[2]; - record.dest_out.w = value[3]; -} - -template <> -inline void SetField<DebugDataRecord::ADDR_REG_OUT>(DebugDataRecord& record, s32* value) { - record.address_registers[0] = value[0]; - record.address_registers[1] = value[1]; -} - -template <> -inline void SetField<DebugDataRecord::CMP_RESULT>(DebugDataRecord& record, bool* value) { - record.conditional_code[0] = value[0]; - record.conditional_code[1] = value[1]; -} - -template <> -inline void SetField<DebugDataRecord::COND_BOOL_IN>(DebugDataRecord& record, bool value) { - record.cond_bool = value; -} - -template <> -inline void SetField<DebugDataRecord::COND_CMP_IN>(DebugDataRecord& record, bool* value) { - record.cond_cmp[0] = value[0]; - record.cond_cmp[1] = value[1]; -} - -template <> -inline void SetField<DebugDataRecord::LOOP_INT_IN>(DebugDataRecord& record, Math::Vec4<u8> value) { - record.loop_int = value; -} - -template <> -inline void SetField<DebugDataRecord::CUR_INSTR>(DebugDataRecord& record, u32 value) { - record.instruction_offset = value; -} - -template <> -inline void SetField<DebugDataRecord::NEXT_INSTR>(DebugDataRecord& record, u32 value) { - record.next_instruction = value; -} - -// Helper function to set debug information on the current shader iteration. -template <DebugDataRecord::Type type, typename ValueType> -inline void Record(DebugData<false>& debug_data, u32 offset, ValueType value) { - // Debugging disabled => nothing to do -} - -template <DebugDataRecord::Type type, typename ValueType> -inline void Record(DebugData<true>& debug_data, u32 offset, ValueType value) { - if (offset >= debug_data.records.size()) - debug_data.records.resize(offset + 1); - - SetField<type, ValueType>(debug_data.records[offset], value); - debug_data.records[offset].mask |= type; -} - /** * This structure contains the state information that needs to be unique for a shader unit. The 3DS * has four shader units that process shaders in parallel. At the present, Citra only implements a * single shader unit that processes all shaders serially. Putting the state information in a struct * here will make it easier for us to parallelize the shader processing later. */ -template <bool Debug> struct UnitState { struct Registers { // The registers are accessed by the shader JIT using SSE instructions, and are therefore @@ -283,8 +111,6 @@ struct UnitState { // TODO: How many bits do these actually have? s32 address_registers[3]; - DebugData<Debug> debug; - static size_t InputOffset(const SourceRegister& reg) { switch (reg.GetRegisterType()) { case RegisterType::Input: @@ -332,21 +158,16 @@ struct ShaderSetup { std::array<Math::Vec4<u8>, 4> i; } uniforms; - static size_t UniformOffset(RegisterType type, unsigned index) { - switch (type) { - case RegisterType::FloatUniform: - return offsetof(ShaderSetup, uniforms.f) + index * sizeof(Math::Vec4<float24>); - - case RegisterType::BoolUniform: - return offsetof(ShaderSetup, uniforms.b) + index * sizeof(bool); + static size_t GetFloatUniformOffset(unsigned index) { + return offsetof(ShaderSetup, uniforms.f) + index * sizeof(Math::Vec4<float24>); + } - case RegisterType::IntUniform: - return offsetof(ShaderSetup, uniforms.i) + index * sizeof(Math::Vec4<u8>); + static size_t GetBoolUniformOffset(unsigned index) { + return offsetof(ShaderSetup, uniforms.b) + index * sizeof(bool); + } - default: - UNREACHABLE(); - return 0; - } + static size_t GetIntUniformOffset(unsigned index) { + return offsetof(ShaderSetup, uniforms.i) + index * sizeof(Math::Vec4<u8>); } std::array<u32, 1024> program_code; @@ -364,7 +185,7 @@ struct ShaderSetup { * @param input Input vertex into the shader * @param num_attributes The number of vertex shader attributes */ - void Run(UnitState<false>& state, const InputVertex& input, int num_attributes); + void Run(UnitState& state, const InputVertex& input, int num_attributes); /** * Produce debug information based on the given shader and input vertex diff --git a/src/video_core/shader/shader_interpreter.cpp b/src/video_core/shader/shader_interpreter.cpp index 6abb6761f..70db4167e 100644 --- a/src/video_core/shader/shader_interpreter.cpp +++ b/src/video_core/shader/shader_interpreter.cpp @@ -6,6 +6,7 @@ #include <array> #include <cmath> #include <numeric> +#include <boost/container/static_vector.hpp> #include <nihstro/shader_bytecode.h> #include "common/assert.h" #include "common/common_types.h" @@ -38,12 +39,42 @@ struct CallStackElement { }; template <bool Debug> -void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned offset) { +void RunInterpreter(const ShaderSetup& setup, UnitState& state, DebugData<Debug>& debug_data, + unsigned offset) { // TODO: Is there a maximal size for this? boost::container::static_vector<CallStackElement, 16> call_stack; - u32 program_counter = offset; + auto call = [&program_counter, &call_stack](u32 offset, u32 num_instructions, u32 return_offset, + u8 repeat_count, u8 loop_increment) { + // -1 to make sure when incrementing the PC we end up at the correct offset + program_counter = offset - 1; + ASSERT(call_stack.size() < call_stack.capacity()); + call_stack.push_back( + {offset + num_instructions, return_offset, repeat_count, loop_increment, offset}); + }; + + auto evaluate_condition = [&state](Instruction::FlowControlType flow_control) { + using Op = Instruction::FlowControlType::Op; + + bool result_x = flow_control.refx.Value() == state.conditional_code[0]; + bool result_y = flow_control.refy.Value() == state.conditional_code[1]; + + switch (flow_control.op) { + case Op::Or: + return result_x || result_y; + case Op::And: + return result_x && result_y; + case Op::JustX: + return result_x; + case Op::JustY: + return result_y; + default: + UNREACHABLE(); + return false; + } + }; + const auto& uniforms = g_state.vs.uniforms; const auto& swizzle_data = g_state.vs.swizzle_data; const auto& program_code = g_state.vs.program_code; @@ -74,20 +105,11 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned const Instruction instr = {program_code[program_counter]}; const SwizzlePattern swizzle = {swizzle_data[instr.common.operand_desc_id]}; - auto call = [&program_counter, &call_stack](UnitState<Debug>& state, u32 offset, - u32 num_instructions, u32 return_offset, - u8 repeat_count, u8 loop_increment) { - // -1 to make sure when incrementing the PC we end up at the correct offset - program_counter = offset - 1; - ASSERT(call_stack.size() < call_stack.capacity()); - call_stack.push_back( - {offset + num_instructions, return_offset, repeat_count, loop_increment, offset}); - }; - Record<DebugDataRecord::CUR_INSTR>(state.debug, iteration, program_counter); + Record<DebugDataRecord::CUR_INSTR>(debug_data, iteration, program_counter); if (iteration > 0) - Record<DebugDataRecord::NEXT_INSTR>(state.debug, iteration - 1, program_counter); + Record<DebugDataRecord::NEXT_INSTR>(debug_data, iteration - 1, program_counter); - state.debug.max_offset = std::max<u32>(state.debug.max_offset, 1 + program_counter); + debug_data.max_offset = std::max<u32>(debug_data.max_offset, 1 + program_counter); auto LookupSourceRegister = [&](const SourceRegister& source_reg) -> const float24* { switch (source_reg.GetRegisterType()) { @@ -155,54 +177,54 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned ? &state.registers.temporary[instr.common.dest.Value().GetIndex()][0] : dummy_vec4_float24; - state.debug.max_opdesc_id = - std::max<u32>(state.debug.max_opdesc_id, 1 + instr.common.operand_desc_id); + debug_data.max_opdesc_id = + std::max<u32>(debug_data.max_opdesc_id, 1 + instr.common.operand_desc_id); switch (instr.opcode.Value().EffectiveOpCode()) { case OpCode::Id::ADD: { - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::SRC2>(state.debug, iteration, src2); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::SRC2>(debug_data, iteration, src2); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); for (int i = 0; i < 4; ++i) { if (!swizzle.DestComponentEnabled(i)) continue; dest[i] = src1[i] + src2[i]; } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; } case OpCode::Id::MUL: { - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::SRC2>(state.debug, iteration, src2); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::SRC2>(debug_data, iteration, src2); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); for (int i = 0; i < 4; ++i) { if (!swizzle.DestComponentEnabled(i)) continue; dest[i] = src1[i] * src2[i]; } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; } case OpCode::Id::FLR: - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); for (int i = 0; i < 4; ++i) { if (!swizzle.DestComponentEnabled(i)) continue; dest[i] = float24::FromFloat32(std::floor(src1[i].ToFloat32())); } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; case OpCode::Id::MAX: - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::SRC2>(state.debug, iteration, src2); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::SRC2>(debug_data, iteration, src2); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); for (int i = 0; i < 4; ++i) { if (!swizzle.DestComponentEnabled(i)) continue; @@ -212,13 +234,13 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned // max(NaN, 0) -> 0 dest[i] = (src1[i] > src2[i]) ? src1[i] : src2[i]; } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; case OpCode::Id::MIN: - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::SRC2>(state.debug, iteration, src2); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::SRC2>(debug_data, iteration, src2); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); for (int i = 0; i < 4; ++i) { if (!swizzle.DestComponentEnabled(i)) continue; @@ -228,16 +250,16 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned // min(NaN, 0) -> 0 dest[i] = (src1[i] < src2[i]) ? src1[i] : src2[i]; } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; case OpCode::Id::DP3: case OpCode::Id::DP4: case OpCode::Id::DPH: case OpCode::Id::DPHI: { - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::SRC2>(state.debug, iteration, src2); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::SRC2>(debug_data, iteration, src2); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); OpCode::Id opcode = instr.opcode.Value().EffectiveOpCode(); if (opcode == OpCode::Id::DPH || opcode == OpCode::Id::DPHI) @@ -253,14 +275,14 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned dest[i] = dot; } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; } // Reciprocal case OpCode::Id::RCP: { - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); float24 rcp_res = float24::FromFloat32(1.0f / src1[0].ToFloat32()); for (int i = 0; i < 4; ++i) { if (!swizzle.DestComponentEnabled(i)) @@ -268,14 +290,14 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned dest[i] = rcp_res; } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; } // Reciprocal Square Root case OpCode::Id::RSQ: { - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); float24 rsq_res = float24::FromFloat32(1.0f / std::sqrt(src1[0].ToFloat32())); for (int i = 0; i < 4; ++i) { if (!swizzle.DestComponentEnabled(i)) @@ -283,12 +305,12 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned dest[i] = rsq_res; } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; } case OpCode::Id::MOVA: { - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); for (int i = 0; i < 2; ++i) { if (!swizzle.DestComponentEnabled(i)) continue; @@ -296,29 +318,29 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned // TODO: Figure out how the rounding is done on hardware state.address_registers[i] = static_cast<s32>(src1[i].ToFloat32()); } - Record<DebugDataRecord::ADDR_REG_OUT>(state.debug, iteration, + Record<DebugDataRecord::ADDR_REG_OUT>(debug_data, iteration, state.address_registers); break; } case OpCode::Id::MOV: { - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); for (int i = 0; i < 4; ++i) { if (!swizzle.DestComponentEnabled(i)) continue; dest[i] = src1[i]; } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; } case OpCode::Id::SGE: case OpCode::Id::SGEI: - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::SRC2>(state.debug, iteration, src2); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::SRC2>(debug_data, iteration, src2); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); for (int i = 0; i < 4; ++i) { if (!swizzle.DestComponentEnabled(i)) continue; @@ -326,14 +348,14 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned dest[i] = (src1[i] >= src2[i]) ? float24::FromFloat32(1.0f) : float24::FromFloat32(0.0f); } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; case OpCode::Id::SLT: case OpCode::Id::SLTI: - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::SRC2>(state.debug, iteration, src2); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::SRC2>(debug_data, iteration, src2); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); for (int i = 0; i < 4; ++i) { if (!swizzle.DestComponentEnabled(i)) continue; @@ -341,12 +363,12 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned dest[i] = (src1[i] < src2[i]) ? float24::FromFloat32(1.0f) : float24::FromFloat32(0.0f); } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; case OpCode::Id::CMP: - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::SRC2>(state.debug, iteration, src2); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::SRC2>(debug_data, iteration, src2); for (int i = 0; i < 2; ++i) { // TODO: Can you restrict to one compare via dest masking? @@ -383,12 +405,12 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned break; } } - Record<DebugDataRecord::CMP_RESULT>(state.debug, iteration, state.conditional_code); + Record<DebugDataRecord::CMP_RESULT>(debug_data, iteration, state.conditional_code); break; case OpCode::Id::EX2: { - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); // EX2 only takes first component exp2 and writes it to all dest components float24 ex2_res = float24::FromFloat32(std::exp2(src1[0].ToFloat32())); @@ -399,13 +421,13 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned dest[i] = ex2_res; } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; } case OpCode::Id::LG2: { - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); // LG2 only takes the first component log2 and writes it to all dest components float24 lg2_res = float24::FromFloat32(std::log2(src1[0].ToFloat32())); @@ -416,7 +438,7 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned dest[i] = lg2_res; } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); break; } @@ -498,17 +520,17 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned ? &state.registers.temporary[instr.mad.dest.Value().GetIndex()][0] : dummy_vec4_float24; - Record<DebugDataRecord::SRC1>(state.debug, iteration, src1); - Record<DebugDataRecord::SRC2>(state.debug, iteration, src2); - Record<DebugDataRecord::SRC3>(state.debug, iteration, src3); - Record<DebugDataRecord::DEST_IN>(state.debug, iteration, dest); + Record<DebugDataRecord::SRC1>(debug_data, iteration, src1); + Record<DebugDataRecord::SRC2>(debug_data, iteration, src2); + Record<DebugDataRecord::SRC3>(debug_data, iteration, src3); + Record<DebugDataRecord::DEST_IN>(debug_data, iteration, dest); for (int i = 0; i < 4; ++i) { if (!swizzle.DestComponentEnabled(i)) continue; dest[i] = src1[i] * src2[i] + src3[i]; } - Record<DebugDataRecord::DEST_OUT>(state.debug, iteration, dest); + Record<DebugDataRecord::DEST_OUT>(debug_data, iteration, dest); } else { LOG_ERROR(HW_GPU, "Unhandled multiply-add instruction: 0x%02x (%s): 0x%08x", (int)instr.opcode.Value().EffectiveOpCode(), @@ -518,26 +540,6 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned } default: { - static auto evaluate_condition = [](const UnitState<Debug>& state, bool refx, bool refy, - Instruction::FlowControlType flow_control) { - bool results[2] = {refx == state.conditional_code[0], - refy == state.conditional_code[1]}; - - switch (flow_control.op) { - case flow_control.Or: - return results[0] || results[1]; - - case flow_control.And: - return results[0] && results[1]; - - case flow_control.JustX: - return results[0]; - - case flow_control.JustY: - return results[1]; - } - }; - // Handle each instruction on its own switch (instr.opcode.Value()) { case OpCode::Id::END: @@ -545,17 +547,15 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned break; case OpCode::Id::JMPC: - Record<DebugDataRecord::COND_CMP_IN>(state.debug, iteration, - state.conditional_code); - if (evaluate_condition(state, instr.flow_control.refx, instr.flow_control.refy, - instr.flow_control)) { + Record<DebugDataRecord::COND_CMP_IN>(debug_data, iteration, state.conditional_code); + if (evaluate_condition(instr.flow_control)) { program_counter = instr.flow_control.dest_offset - 1; } break; case OpCode::Id::JMPU: Record<DebugDataRecord::COND_BOOL_IN>( - state.debug, iteration, uniforms.b[instr.flow_control.bool_uniform_id]); + debug_data, iteration, uniforms.b[instr.flow_control.bool_uniform_id]); if (uniforms.b[instr.flow_control.bool_uniform_id] == !(instr.flow_control.num_instructions & 1)) { @@ -564,25 +564,23 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned break; case OpCode::Id::CALL: - call(state, instr.flow_control.dest_offset, instr.flow_control.num_instructions, + call(instr.flow_control.dest_offset, instr.flow_control.num_instructions, program_counter + 1, 0, 0); break; case OpCode::Id::CALLU: Record<DebugDataRecord::COND_BOOL_IN>( - state.debug, iteration, uniforms.b[instr.flow_control.bool_uniform_id]); + debug_data, iteration, uniforms.b[instr.flow_control.bool_uniform_id]); if (uniforms.b[instr.flow_control.bool_uniform_id]) { - call(state, instr.flow_control.dest_offset, instr.flow_control.num_instructions, + call(instr.flow_control.dest_offset, instr.flow_control.num_instructions, program_counter + 1, 0, 0); } break; case OpCode::Id::CALLC: - Record<DebugDataRecord::COND_CMP_IN>(state.debug, iteration, - state.conditional_code); - if (evaluate_condition(state, instr.flow_control.refx, instr.flow_control.refy, - instr.flow_control)) { - call(state, instr.flow_control.dest_offset, instr.flow_control.num_instructions, + Record<DebugDataRecord::COND_CMP_IN>(debug_data, iteration, state.conditional_code); + if (evaluate_condition(instr.flow_control)) { + call(instr.flow_control.dest_offset, instr.flow_control.num_instructions, program_counter + 1, 0, 0); } break; @@ -592,14 +590,13 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned case OpCode::Id::IFU: Record<DebugDataRecord::COND_BOOL_IN>( - state.debug, iteration, uniforms.b[instr.flow_control.bool_uniform_id]); + debug_data, iteration, uniforms.b[instr.flow_control.bool_uniform_id]); if (uniforms.b[instr.flow_control.bool_uniform_id]) { - call(state, program_counter + 1, - instr.flow_control.dest_offset - program_counter - 1, + call(program_counter + 1, instr.flow_control.dest_offset - program_counter - 1, instr.flow_control.dest_offset + instr.flow_control.num_instructions, 0, 0); } else { - call(state, instr.flow_control.dest_offset, instr.flow_control.num_instructions, + call(instr.flow_control.dest_offset, instr.flow_control.num_instructions, instr.flow_control.dest_offset + instr.flow_control.num_instructions, 0, 0); } @@ -609,16 +606,13 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned case OpCode::Id::IFC: { // TODO: Do we need to consider swizzlers here? - Record<DebugDataRecord::COND_CMP_IN>(state.debug, iteration, - state.conditional_code); - if (evaluate_condition(state, instr.flow_control.refx, instr.flow_control.refy, - instr.flow_control)) { - call(state, program_counter + 1, - instr.flow_control.dest_offset - program_counter - 1, + Record<DebugDataRecord::COND_CMP_IN>(debug_data, iteration, state.conditional_code); + if (evaluate_condition(instr.flow_control)) { + call(program_counter + 1, instr.flow_control.dest_offset - program_counter - 1, instr.flow_control.dest_offset + instr.flow_control.num_instructions, 0, 0); } else { - call(state, instr.flow_control.dest_offset, instr.flow_control.num_instructions, + call(instr.flow_control.dest_offset, instr.flow_control.num_instructions, instr.flow_control.dest_offset + instr.flow_control.num_instructions, 0, 0); } @@ -633,9 +627,8 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned uniforms.i[instr.flow_control.int_uniform_id].w); state.address_registers[2] = loop_param.y; - Record<DebugDataRecord::LOOP_INT_IN>(state.debug, iteration, loop_param); - call(state, program_counter + 1, - instr.flow_control.dest_offset - program_counter + 1, + Record<DebugDataRecord::LOOP_INT_IN>(debug_data, iteration, loop_param); + call(program_counter + 1, instr.flow_control.dest_offset - program_counter + 1, instr.flow_control.dest_offset + 1, loop_param.x, loop_param.z); break; } @@ -657,8 +650,8 @@ void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned } // Explicit instantiation -template void RunInterpreter(const ShaderSetup& setup, UnitState<false>& state, unsigned offset); -template void RunInterpreter(const ShaderSetup& setup, UnitState<true>& state, unsigned offset); +template void RunInterpreter(const ShaderSetup&, UnitState&, DebugData<false>&, unsigned offset); +template void RunInterpreter(const ShaderSetup&, UnitState&, DebugData<true>&, unsigned offset); } // namespace diff --git a/src/video_core/shader/shader_interpreter.h b/src/video_core/shader/shader_interpreter.h index 48ede0a2e..d31dcd7a6 100644 --- a/src/video_core/shader/shader_interpreter.h +++ b/src/video_core/shader/shader_interpreter.h @@ -8,11 +8,14 @@ namespace Pica { namespace Shader { -template <bool Debug> struct UnitState; template <bool Debug> -void RunInterpreter(const ShaderSetup& setup, UnitState<Debug>& state, unsigned offset); +struct DebugData; + +template <bool Debug> +void RunInterpreter(const ShaderSetup& setup, UnitState& state, DebugData<Debug>& debug_data, + unsigned offset); } // namespace diff --git a/src/video_core/shader/shader_jit_x64.cpp b/src/video_core/shader/shader_jit_x64.cpp index c96110bb2..c588b778b 100644 --- a/src/video_core/shader/shader_jit_x64.cpp +++ b/src/video_core/shader/shader_jit_x64.cpp @@ -6,24 +6,30 @@ #include <cmath> #include <cstdint> #include <nihstro/shader_bytecode.h> +#include <smmintrin.h> #include <xmmintrin.h> #include "common/assert.h" #include "common/logging/log.h" #include "common/vector_math.h" -#include "common/x64/abi.h" #include "common/x64/cpu_detect.h" -#include "common/x64/emitter.h" -#include "shader.h" -#include "shader_jit_x64.h" +#include "common/x64/xbyak_abi.h" +#include "common/x64/xbyak_util.h" #include "video_core/pica_state.h" #include "video_core/pica_types.h" +#include "video_core/shader/shader.h" +#include "video_core/shader/shader_jit_x64.h" + +using namespace Common::X64; +using namespace Xbyak::util; +using Xbyak::Label; +using Xbyak::Reg32; +using Xbyak::Reg64; +using Xbyak::Xmm; namespace Pica { namespace Shader { -using namespace Gen; - typedef void (JitShader::*JitFunction)(Instruction instr); const JitFunction instr_table[64] = { @@ -98,44 +104,47 @@ const JitFunction instr_table[64] = { // purposes, as documented below: /// Pointer to the uniform memory -static const X64Reg SETUP = R9; +static const Reg64 SETUP = r9; /// The two 32-bit VS address offset registers set by the MOVA instruction -static const X64Reg ADDROFFS_REG_0 = R10; -static const X64Reg ADDROFFS_REG_1 = R11; +static const Reg64 ADDROFFS_REG_0 = r10; +static const Reg64 ADDROFFS_REG_1 = r11; /// VS loop count register (Multiplied by 16) -static const X64Reg LOOPCOUNT_REG = R12; +static const Reg32 LOOPCOUNT_REG = r12d; /// Current VS loop iteration number (we could probably use LOOPCOUNT_REG, but this quicker) -static const X64Reg LOOPCOUNT = RSI; +static const Reg32 LOOPCOUNT = esi; /// Number to increment LOOPCOUNT_REG by on each loop iteration (Multiplied by 16) -static const X64Reg LOOPINC = RDI; +static const Reg32 LOOPINC = edi; /// Result of the previous CMP instruction for the X-component comparison -static const X64Reg COND0 = R13; +static const Reg64 COND0 = r13; /// Result of the previous CMP instruction for the Y-component comparison -static const X64Reg COND1 = R14; +static const Reg64 COND1 = r14; /// Pointer to the UnitState instance for the current VS unit -static const X64Reg STATE = R15; +static const Reg64 STATE = r15; /// SIMD scratch register -static const X64Reg SCRATCH = XMM0; +static const Xmm SCRATCH = xmm0; /// Loaded with the first swizzled source register, otherwise can be used as a scratch register -static const X64Reg SRC1 = XMM1; +static const Xmm SRC1 = xmm1; /// Loaded with the second swizzled source register, otherwise can be used as a scratch register -static const X64Reg SRC2 = XMM2; +static const Xmm SRC2 = xmm2; /// Loaded with the third swizzled source register, otherwise can be used as a scratch register -static const X64Reg SRC3 = XMM3; +static const Xmm SRC3 = xmm3; /// Additional scratch register -static const X64Reg SCRATCH2 = XMM4; +static const Xmm SCRATCH2 = xmm4; /// Constant vector of [1.0f, 1.0f, 1.0f, 1.0f], used to efficiently set a vector to one -static const X64Reg ONE = XMM14; +static const Xmm ONE = xmm14; /// Constant vector of [-0.f, -0.f, -0.f, -0.f], used to efficiently negate a vector with XOR -static const X64Reg NEGBIT = XMM15; +static const Xmm NEGBIT = xmm15; // State registers that must not be modified by external functions calls // Scratch registers, e.g., SRC1 and SCRATCH, have to be saved on the side if needed -static const BitSet32 persistent_regs = { - SETUP, STATE, // Pointers to register blocks - ADDROFFS_REG_0, ADDROFFS_REG_1, LOOPCOUNT_REG, COND0, COND1, // Cached registers - ONE + 16, NEGBIT + 16, // Constants -}; +static const BitSet32 persistent_regs = BuildRegSet({ + // Pointers to register blocks + SETUP, STATE, + // Cached registers + ADDROFFS_REG_0, ADDROFFS_REG_1, LOOPCOUNT_REG, COND0, COND1, + // Constants + ONE, NEGBIT, +}); /// Raw constant for the source register selector that indicates no swizzling is performed static const u8 NO_SRC_REG_SWIZZLE = 0x1b; @@ -157,7 +166,8 @@ static void LogCritical(const char* msg) { void JitShader::Compile_Assert(bool condition, const char* msg) { if (!condition) { - ABI_CallFunctionP(reinterpret_cast<const void*>(LogCritical), const_cast<char*>(msg)); + mov(ABI_PARAM1, reinterpret_cast<size_t>(msg)); + CallFarFunction(*this, LogCritical); } } @@ -169,16 +179,16 @@ void JitShader::Compile_Assert(bool condition, const char* msg) { * @param dest Destination XMM register to store the loaded, swizzled source register */ void JitShader::Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRegister src_reg, - X64Reg dest) { - X64Reg src_ptr; + Xmm dest) { + Reg64 src_ptr; size_t src_offset; if (src_reg.GetRegisterType() == RegisterType::FloatUniform) { src_ptr = SETUP; - src_offset = ShaderSetup::UniformOffset(RegisterType::FloatUniform, src_reg.GetIndex()); + src_offset = ShaderSetup::GetFloatUniformOffset(src_reg.GetIndex()); } else { src_ptr = STATE; - src_offset = UnitState<false>::InputOffset(src_reg); + src_offset = UnitState::InputOffset(src_reg); } int src_offset_disp = (int)src_offset; @@ -206,13 +216,13 @@ void JitShader::Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRe if (src_num == offset_src && address_register_index != 0) { switch (address_register_index) { case 1: // address offset 1 - MOVAPS(dest, MComplex(src_ptr, ADDROFFS_REG_0, SCALE_1, src_offset_disp)); + movaps(dest, xword[src_ptr + ADDROFFS_REG_0 + src_offset_disp]); break; case 2: // address offset 2 - MOVAPS(dest, MComplex(src_ptr, ADDROFFS_REG_1, SCALE_1, src_offset_disp)); + movaps(dest, xword[src_ptr + ADDROFFS_REG_1 + src_offset_disp]); break; case 3: // address offset 3 - MOVAPS(dest, MComplex(src_ptr, LOOPCOUNT_REG, SCALE_1, src_offset_disp)); + movaps(dest, xword[src_ptr + LOOPCOUNT_REG.cvt64() + src_offset_disp]); break; default: UNREACHABLE(); @@ -220,7 +230,7 @@ void JitShader::Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRe } } else { // Load the source - MOVAPS(dest, MDisp(src_ptr, src_offset_disp)); + movaps(dest, xword[src_ptr + src_offset_disp]); } SwizzlePattern swiz = {g_state.vs.swizzle_data[operand_desc_id]}; @@ -232,17 +242,17 @@ void JitShader::Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRe sel = ((sel & 0xc0) >> 6) | ((sel & 3) << 6) | ((sel & 0xc) << 2) | ((sel & 0x30) >> 2); // Shuffle inputs for swizzle - SHUFPS(dest, R(dest), sel); + shufps(dest, dest, sel); } // If the source register should be negated, flip the negative bit using XOR const bool negate[] = {swiz.negate_src1, swiz.negate_src2, swiz.negate_src3}; if (negate[src_num - 1]) { - XORPS(dest, R(NEGBIT)); + xorps(dest, NEGBIT); } } -void JitShader::Compile_DestEnable(Instruction instr, X64Reg src) { +void JitShader::Compile_DestEnable(Instruction instr, Xmm src) { DestRegister dest; unsigned operand_desc_id; if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::MAD || @@ -256,28 +266,26 @@ void JitShader::Compile_DestEnable(Instruction instr, X64Reg src) { SwizzlePattern swiz = {g_state.vs.swizzle_data[operand_desc_id]}; - int dest_offset_disp = (int)UnitState<false>::OutputOffset(dest); - ASSERT_MSG(dest_offset_disp == UnitState<false>::OutputOffset(dest), - "Destinaton offset too large for int type"); + size_t dest_offset_disp = UnitState::OutputOffset(dest); // If all components are enabled, write the result to the destination register if (swiz.dest_mask == NO_DEST_REG_MASK) { // Store dest back to memory - MOVAPS(MDisp(STATE, dest_offset_disp), src); + movaps(xword[STATE + dest_offset_disp], src); } else { // Not all components are enabled, so mask the result when storing to the destination // register... - MOVAPS(SCRATCH, MDisp(STATE, dest_offset_disp)); + movaps(SCRATCH, xword[STATE + dest_offset_disp]); if (Common::GetCPUCaps().sse4_1) { u8 mask = ((swiz.dest_mask & 1) << 3) | ((swiz.dest_mask & 8) >> 3) | ((swiz.dest_mask & 2) << 1) | ((swiz.dest_mask & 4) >> 1); - BLENDPS(SCRATCH, R(src), mask); + blendps(SCRATCH, src, mask); } else { - MOVAPS(SCRATCH2, R(src)); - UNPCKHPS(SCRATCH2, R(SCRATCH)); // Unpack X/Y components of source and destination - UNPCKLPS(SCRATCH, R(src)); // Unpack Z/W components of source and destination + movaps(SCRATCH2, src); + unpckhps(SCRATCH2, SCRATCH); // Unpack X/Y components of source and destination + unpcklps(SCRATCH, src); // Unpack Z/W components of source and destination // Compute selector to selectively copy source components to destination for SHUFPS // instruction @@ -285,62 +293,61 @@ void JitShader::Compile_DestEnable(Instruction instr, X64Reg src) { ((swiz.DestComponentEnabled(1) ? 3 : 2) << 2) | ((swiz.DestComponentEnabled(2) ? 0 : 1) << 4) | ((swiz.DestComponentEnabled(3) ? 2 : 3) << 6); - SHUFPS(SCRATCH, R(SCRATCH2), sel); + shufps(SCRATCH, SCRATCH2, sel); } // Store dest back to memory - MOVAPS(MDisp(STATE, dest_offset_disp), SCRATCH); + movaps(xword[STATE + dest_offset_disp], SCRATCH); } } -void JitShader::Compile_SanitizedMul(Gen::X64Reg src1, Gen::X64Reg src2, Gen::X64Reg scratch) { - MOVAPS(scratch, R(src1)); - CMPPS(scratch, R(src2), CMP_ORD); +void JitShader::Compile_SanitizedMul(Xmm src1, Xmm src2, Xmm scratch) { + movaps(scratch, src1); + cmpordps(scratch, src2); - MULPS(src1, R(src2)); + mulps(src1, src2); - MOVAPS(src2, R(src1)); - CMPPS(src2, R(src2), CMP_UNORD); + movaps(src2, src1); + cmpunordps(src2, src2); - XORPS(scratch, R(src2)); - ANDPS(src1, R(scratch)); + xorps(scratch, src2); + andps(src1, scratch); } void JitShader::Compile_EvaluateCondition(Instruction instr) { // Note: NXOR is used below to check for equality switch (instr.flow_control.op) { case Instruction::FlowControlType::Or: - MOV(32, R(RAX), R(COND0)); - MOV(32, R(RBX), R(COND1)); - XOR(32, R(RAX), Imm32(instr.flow_control.refx.Value() ^ 1)); - XOR(32, R(RBX), Imm32(instr.flow_control.refy.Value() ^ 1)); - OR(32, R(RAX), R(RBX)); + mov(eax, COND0); + mov(ebx, COND1); + xor(eax, (instr.flow_control.refx.Value() ^ 1)); + xor(ebx, (instr.flow_control.refy.Value() ^ 1)); + or (eax, ebx); break; case Instruction::FlowControlType::And: - MOV(32, R(RAX), R(COND0)); - MOV(32, R(RBX), R(COND1)); - XOR(32, R(RAX), Imm32(instr.flow_control.refx.Value() ^ 1)); - XOR(32, R(RBX), Imm32(instr.flow_control.refy.Value() ^ 1)); - AND(32, R(RAX), R(RBX)); + mov(eax, COND0); + mov(ebx, COND1); + xor(eax, (instr.flow_control.refx.Value() ^ 1)); + xor(ebx, (instr.flow_control.refy.Value() ^ 1)); + and(eax, ebx); break; case Instruction::FlowControlType::JustX: - MOV(32, R(RAX), R(COND0)); - XOR(32, R(RAX), Imm32(instr.flow_control.refx.Value() ^ 1)); + mov(eax, COND0); + xor(eax, (instr.flow_control.refx.Value() ^ 1)); break; case Instruction::FlowControlType::JustY: - MOV(32, R(RAX), R(COND1)); - XOR(32, R(RAX), Imm32(instr.flow_control.refy.Value() ^ 1)); + mov(eax, COND1); + xor(eax, (instr.flow_control.refy.Value() ^ 1)); break; } } void JitShader::Compile_UniformCondition(Instruction instr) { - int offset = - ShaderSetup::UniformOffset(RegisterType::BoolUniform, instr.flow_control.bool_uniform_id); - CMP(sizeof(bool) * 8, MDisp(SETUP, offset), Imm8(0)); + size_t offset = ShaderSetup::GetBoolUniformOffset(instr.flow_control.bool_uniform_id); + cmp(byte[SETUP + offset], 0); } BitSet32 JitShader::PersistentCallerSavedRegs() { @@ -350,7 +357,7 @@ BitSet32 JitShader::PersistentCallerSavedRegs() { void JitShader::Compile_ADD(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2); - ADDPS(SRC1, R(SRC2)); + addps(SRC1, SRC2); Compile_DestEnable(instr, SRC1); } @@ -360,15 +367,15 @@ void JitShader::Compile_DP3(Instruction instr) { Compile_SanitizedMul(SRC1, SRC2, SCRATCH); - MOVAPS(SRC2, R(SRC1)); - SHUFPS(SRC2, R(SRC2), _MM_SHUFFLE(1, 1, 1, 1)); + movaps(SRC2, SRC1); + shufps(SRC2, SRC2, _MM_SHUFFLE(1, 1, 1, 1)); - MOVAPS(SRC3, R(SRC1)); - SHUFPS(SRC3, R(SRC3), _MM_SHUFFLE(2, 2, 2, 2)); + movaps(SRC3, SRC1); + shufps(SRC3, SRC3, _MM_SHUFFLE(2, 2, 2, 2)); - SHUFPS(SRC1, R(SRC1), _MM_SHUFFLE(0, 0, 0, 0)); - ADDPS(SRC1, R(SRC2)); - ADDPS(SRC1, R(SRC3)); + shufps(SRC1, SRC1, _MM_SHUFFLE(0, 0, 0, 0)); + addps(SRC1, SRC2); + addps(SRC1, SRC3); Compile_DestEnable(instr, SRC1); } @@ -379,13 +386,13 @@ void JitShader::Compile_DP4(Instruction instr) { Compile_SanitizedMul(SRC1, SRC2, SCRATCH); - MOVAPS(SRC2, R(SRC1)); - SHUFPS(SRC1, R(SRC1), _MM_SHUFFLE(2, 3, 0, 1)); // XYZW -> ZWXY - ADDPS(SRC1, R(SRC2)); + movaps(SRC2, SRC1); + shufps(SRC1, SRC1, _MM_SHUFFLE(2, 3, 0, 1)); // XYZW -> ZWXY + addps(SRC1, SRC2); - MOVAPS(SRC2, R(SRC1)); - SHUFPS(SRC1, R(SRC1), _MM_SHUFFLE(0, 1, 2, 3)); // XYZW -> WZYX - ADDPS(SRC1, R(SRC2)); + movaps(SRC2, SRC1); + shufps(SRC1, SRC1, _MM_SHUFFLE(0, 1, 2, 3)); // XYZW -> WZYX + addps(SRC1, SRC2); Compile_DestEnable(instr, SRC1); } @@ -401,50 +408,50 @@ void JitShader::Compile_DPH(Instruction instr) { if (Common::GetCPUCaps().sse4_1) { // Set 4th component to 1.0 - BLENDPS(SRC1, R(ONE), 0x8); // 0b1000 + blendps(SRC1, ONE, 0b1000); } else { // Set 4th component to 1.0 - MOVAPS(SCRATCH, R(SRC1)); - UNPCKHPS(SCRATCH, R(ONE)); // XYZW, 1111 -> Z1__ - UNPCKLPD(SRC1, R(SCRATCH)); // XYZW, Z1__ -> XYZ1 + movaps(SCRATCH, SRC1); + unpckhps(SCRATCH, ONE); // XYZW, 1111 -> Z1__ + unpcklpd(SRC1, SCRATCH); // XYZW, Z1__ -> XYZ1 } Compile_SanitizedMul(SRC1, SRC2, SCRATCH); - MOVAPS(SRC2, R(SRC1)); - SHUFPS(SRC1, R(SRC1), _MM_SHUFFLE(2, 3, 0, 1)); // XYZW -> ZWXY - ADDPS(SRC1, R(SRC2)); + movaps(SRC2, SRC1); + shufps(SRC1, SRC1, _MM_SHUFFLE(2, 3, 0, 1)); // XYZW -> ZWXY + addps(SRC1, SRC2); - MOVAPS(SRC2, R(SRC1)); - SHUFPS(SRC1, R(SRC1), _MM_SHUFFLE(0, 1, 2, 3)); // XYZW -> WZYX - ADDPS(SRC1, R(SRC2)); + movaps(SRC2, SRC1); + shufps(SRC1, SRC1, _MM_SHUFFLE(0, 1, 2, 3)); // XYZW -> WZYX + addps(SRC1, SRC2); Compile_DestEnable(instr, SRC1); } void JitShader::Compile_EX2(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); - MOVSS(XMM0, R(SRC1)); + movss(xmm0, SRC1); // ABI_PARAM1 - ABI_PushRegistersAndAdjustStack(PersistentCallerSavedRegs(), 0); - ABI_CallFunction(reinterpret_cast<const void*>(exp2f)); - ABI_PopRegistersAndAdjustStack(PersistentCallerSavedRegs(), 0); + ABI_PushRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0); + CallFarFunction(*this, exp2f); + ABI_PopRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0); - SHUFPS(XMM0, R(XMM0), _MM_SHUFFLE(0, 0, 0, 0)); - MOVAPS(SRC1, R(XMM0)); + shufps(xmm0, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); // ABI_RETURN + movaps(SRC1, xmm0); Compile_DestEnable(instr, SRC1); } void JitShader::Compile_LG2(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); - MOVSS(XMM0, R(SRC1)); + movss(xmm0, SRC1); // ABI_PARAM1 - ABI_PushRegistersAndAdjustStack(PersistentCallerSavedRegs(), 0); - ABI_CallFunction(reinterpret_cast<const void*>(log2f)); - ABI_PopRegistersAndAdjustStack(PersistentCallerSavedRegs(), 0); + ABI_PushRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0); + CallFarFunction(*this, log2f); + ABI_PopRegistersAndAdjustStack(*this, PersistentCallerSavedRegs(), 0); - SHUFPS(XMM0, R(XMM0), _MM_SHUFFLE(0, 0, 0, 0)); - MOVAPS(SRC1, R(XMM0)); + shufps(xmm0, xmm0, _MM_SHUFFLE(0, 0, 0, 0)); // ABI_RETURN + movaps(SRC1, xmm0); Compile_DestEnable(instr, SRC1); } @@ -464,8 +471,8 @@ void JitShader::Compile_SGE(Instruction instr) { Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2); } - CMPPS(SRC2, R(SRC1), CMP_LE); - ANDPS(SRC2, R(ONE)); + cmpleps(SRC2, SRC1); + andps(SRC2, ONE); Compile_DestEnable(instr, SRC2); } @@ -479,8 +486,8 @@ void JitShader::Compile_SLT(Instruction instr) { Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2); } - CMPPS(SRC1, R(SRC2), CMP_LT); - ANDPS(SRC1, R(ONE)); + cmpltps(SRC1, SRC2); + andps(SRC1, ONE); Compile_DestEnable(instr, SRC1); } @@ -489,10 +496,10 @@ void JitShader::Compile_FLR(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); if (Common::GetCPUCaps().sse4_1) { - ROUNDFLOORPS(SRC1, R(SRC1)); + roundps(SRC1, SRC1, _MM_FROUND_FLOOR); } else { - CVTTPS2DQ(SRC1, R(SRC1)); - CVTDQ2PS(SRC1, R(SRC1)); + cvttps2dq(SRC1, SRC1); + cvtdq2ps(SRC1, SRC1); } Compile_DestEnable(instr, SRC1); @@ -502,7 +509,7 @@ void JitShader::Compile_MAX(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2); // SSE semantics match PICA200 ones: In case of NaN, SRC2 is returned. - MAXPS(SRC1, R(SRC2)); + maxps(SRC1, SRC2); Compile_DestEnable(instr, SRC1); } @@ -510,7 +517,7 @@ void JitShader::Compile_MIN(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); Compile_SwizzleSrc(instr, 2, instr.common.src2, SRC2); // SSE semantics match PICA200 ones: In case of NaN, SRC2 is returned. - MINPS(SRC1, R(SRC2)); + minps(SRC1, SRC2); Compile_DestEnable(instr, SRC1); } @@ -524,37 +531,37 @@ void JitShader::Compile_MOVA(Instruction instr) { Compile_SwizzleSrc(instr, 1, instr.common.src1, SRC1); // Convert floats to integers using truncation (only care about X and Y components) - CVTTPS2DQ(SRC1, R(SRC1)); + cvttps2dq(SRC1, SRC1); // Get result - MOVQ_xmm(R(RAX), SRC1); + movq(rax, SRC1); // Handle destination enable if (swiz.DestComponentEnabled(0) && swiz.DestComponentEnabled(1)) { // Move and sign-extend low 32 bits - MOVSX(64, 32, ADDROFFS_REG_0, R(RAX)); + movsxd(ADDROFFS_REG_0, eax); // Move and sign-extend high 32 bits - SHR(64, R(RAX), Imm8(32)); - MOVSX(64, 32, ADDROFFS_REG_1, R(RAX)); + shr(rax, 32); + movsxd(ADDROFFS_REG_1, eax); // Multiply by 16 to be used as an offset later - SHL(64, R(ADDROFFS_REG_0), Imm8(4)); - SHL(64, R(ADDROFFS_REG_1), Imm8(4)); + shl(ADDROFFS_REG_0, 4); + shl(ADDROFFS_REG_1, 4); } else { if (swiz.DestComponentEnabled(0)) { // Move and sign-extend low 32 bits - MOVSX(64, 32, ADDROFFS_REG_0, R(RAX)); + movsxd(ADDROFFS_REG_0, eax); // Multiply by 16 to be used as an offset later - SHL(64, R(ADDROFFS_REG_0), Imm8(4)); + shl(ADDROFFS_REG_0, 4); } else if (swiz.DestComponentEnabled(1)) { // Move and sign-extend high 32 bits - SHR(64, R(RAX), Imm8(32)); - MOVSX(64, 32, ADDROFFS_REG_1, R(RAX)); + shr(rax, 32); + movsxd(ADDROFFS_REG_1, eax); // Multiply by 16 to be used as an offset later - SHL(64, R(ADDROFFS_REG_1), Imm8(4)); + shl(ADDROFFS_REG_1, 4); } } } @@ -569,8 +576,8 @@ void JitShader::Compile_RCP(Instruction instr) { // TODO(bunnei): RCPSS is a pretty rough approximation, this might cause problems if Pica // performs this operation more accurately. This should be checked on hardware. - RCPSS(SRC1, R(SRC1)); - SHUFPS(SRC1, R(SRC1), _MM_SHUFFLE(0, 0, 0, 0)); // XYWZ -> XXXX + rcpss(SRC1, SRC1); + shufps(SRC1, SRC1, _MM_SHUFFLE(0, 0, 0, 0)); // XYWZ -> XXXX Compile_DestEnable(instr, SRC1); } @@ -580,8 +587,8 @@ void JitShader::Compile_RSQ(Instruction instr) { // TODO(bunnei): RSQRTSS is a pretty rough approximation, this might cause problems if Pica // performs this operation more accurately. This should be checked on hardware. - RSQRTSS(SRC1, R(SRC1)); - SHUFPS(SRC1, R(SRC1), _MM_SHUFFLE(0, 0, 0, 0)); // XYWZ -> XXXX + rsqrtss(SRC1, SRC1); + shufps(SRC1, SRC1, _MM_SHUFFLE(0, 0, 0, 0)); // XYWZ -> XXXX Compile_DestEnable(instr, SRC1); } @@ -589,34 +596,35 @@ void JitShader::Compile_RSQ(Instruction instr) { void JitShader::Compile_NOP(Instruction instr) {} void JitShader::Compile_END(Instruction instr) { - ABI_PopRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8); - RET(); + ABI_PopRegistersAndAdjustStack(*this, ABI_ALL_CALLEE_SAVED, 8); + ret(); } void JitShader::Compile_CALL(Instruction instr) { // Push offset of the return - PUSH(64, Imm32(instr.flow_control.dest_offset + instr.flow_control.num_instructions)); + push(qword, (instr.flow_control.dest_offset + instr.flow_control.num_instructions)); // Call the subroutine - FixupBranch b = CALL(); - fixup_branches.push_back({b, instr.flow_control.dest_offset}); + call(instruction_labels[instr.flow_control.dest_offset]); // Skip over the return offset that's on the stack - ADD(64, R(RSP), Imm32(8)); + add(rsp, 8); } void JitShader::Compile_CALLC(Instruction instr) { Compile_EvaluateCondition(instr); - FixupBranch b = J_CC(CC_Z, true); + Label b; + jz(b); Compile_CALL(instr); - SetJumpTarget(b); + L(b); } void JitShader::Compile_CALLU(Instruction instr) { Compile_UniformCondition(instr); - FixupBranch b = J_CC(CC_Z, true); + Label b; + jz(b); Compile_CALL(instr); - SetJumpTarget(b); + L(b); } void JitShader::Compile_CMP(Instruction instr) { @@ -633,33 +641,33 @@ void JitShader::Compile_CMP(Instruction instr) { static const u8 cmp[] = {CMP_EQ, CMP_NEQ, CMP_LT, CMP_LE, CMP_LT, CMP_LE}; bool invert_op_x = (op_x == Op::GreaterThan || op_x == Op::GreaterEqual); - Gen::X64Reg lhs_x = invert_op_x ? SRC2 : SRC1; - Gen::X64Reg rhs_x = invert_op_x ? SRC1 : SRC2; + Xmm lhs_x = invert_op_x ? SRC2 : SRC1; + Xmm rhs_x = invert_op_x ? SRC1 : SRC2; if (op_x == op_y) { // Compare X-component and Y-component together - CMPPS(lhs_x, R(rhs_x), cmp[op_x]); - MOVQ_xmm(R(COND0), lhs_x); + cmpps(lhs_x, rhs_x, cmp[op_x]); + movq(COND0, lhs_x); - MOV(64, R(COND1), R(COND0)); + mov(COND1, COND0); } else { bool invert_op_y = (op_y == Op::GreaterThan || op_y == Op::GreaterEqual); - Gen::X64Reg lhs_y = invert_op_y ? SRC2 : SRC1; - Gen::X64Reg rhs_y = invert_op_y ? SRC1 : SRC2; + Xmm lhs_y = invert_op_y ? SRC2 : SRC1; + Xmm rhs_y = invert_op_y ? SRC1 : SRC2; // Compare X-component - MOVAPS(SCRATCH, R(lhs_x)); - CMPSS(SCRATCH, R(rhs_x), cmp[op_x]); + movaps(SCRATCH, lhs_x); + cmpss(SCRATCH, rhs_x, cmp[op_x]); // Compare Y-component - CMPPS(lhs_y, R(rhs_y), cmp[op_y]); + cmpps(lhs_y, rhs_y, cmp[op_y]); - MOVQ_xmm(R(COND0), SCRATCH); - MOVQ_xmm(R(COND1), lhs_y); + movq(COND0, SCRATCH); + movq(COND1, lhs_y); } - SHR(32, R(COND0), Imm8(31)); - SHR(64, R(COND1), Imm8(63)); + shr(COND0.cvt32(), 31); // ignores upper 32 bits in source + shr(COND1, 63); } void JitShader::Compile_MAD(Instruction instr) { @@ -674,7 +682,7 @@ void JitShader::Compile_MAD(Instruction instr) { } Compile_SanitizedMul(SRC1, SRC2, SCRATCH); - ADDPS(SRC1, R(SRC3)); + addps(SRC1, SRC3); Compile_DestEnable(instr, SRC1); } @@ -682,6 +690,7 @@ void JitShader::Compile_MAD(Instruction instr) { void JitShader::Compile_IF(Instruction instr) { Compile_Assert(instr.flow_control.dest_offset >= program_counter, "Backwards if-statements not supported"); + Label l_else, l_endif; // Evaluate the "IF" condition if (instr.opcode.Value() == OpCode::Id::IFU) { @@ -689,26 +698,25 @@ void JitShader::Compile_IF(Instruction instr) { } else if (instr.opcode.Value() == OpCode::Id::IFC) { Compile_EvaluateCondition(instr); } - FixupBranch b = J_CC(CC_Z, true); + jz(l_else, T_NEAR); // Compile the code that corresponds to the condition evaluating as true Compile_Block(instr.flow_control.dest_offset); // If there isn't an "ELSE" condition, we are done here if (instr.flow_control.num_instructions == 0) { - SetJumpTarget(b); + L(l_else); return; } - FixupBranch b2 = J(true); - - SetJumpTarget(b); + jmp(l_endif, T_NEAR); + L(l_else); // This code corresponds to the "ELSE" condition // Comple the code that corresponds to the condition evaluating as false Compile_Block(instr.flow_control.dest_offset + instr.flow_control.num_instructions); - SetJumpTarget(b2); + L(l_endif); } void JitShader::Compile_LOOP(Instruction instr) { @@ -721,25 +729,25 @@ void JitShader::Compile_LOOP(Instruction instr) { // This decodes the fields from the integer uniform at index instr.flow_control.int_uniform_id. // The Y (LOOPCOUNT_REG) and Z (LOOPINC) component are kept multiplied by 16 (Left shifted by // 4 bits) to be used as an offset into the 16-byte vector registers later - int offset = - ShaderSetup::UniformOffset(RegisterType::IntUniform, instr.flow_control.int_uniform_id); - MOV(32, R(LOOPCOUNT), MDisp(SETUP, offset)); - MOV(32, R(LOOPCOUNT_REG), R(LOOPCOUNT)); - SHR(32, R(LOOPCOUNT_REG), Imm8(4)); - AND(32, R(LOOPCOUNT_REG), Imm32(0xFF0)); // Y-component is the start - MOV(32, R(LOOPINC), R(LOOPCOUNT)); - SHR(32, R(LOOPINC), Imm8(12)); - AND(32, R(LOOPINC), Imm32(0xFF0)); // Z-component is the incrementer - MOVZX(32, 8, LOOPCOUNT, R(LOOPCOUNT)); // X-component is iteration count - ADD(32, R(LOOPCOUNT), Imm8(1)); // Iteration count is X-component + 1 - - auto loop_start = GetCodePtr(); + size_t offset = ShaderSetup::GetIntUniformOffset(instr.flow_control.int_uniform_id); + mov(LOOPCOUNT, dword[SETUP + offset]); + mov(LOOPCOUNT_REG, LOOPCOUNT); + shr(LOOPCOUNT_REG, 4); + and(LOOPCOUNT_REG, 0xFF0); // Y-component is the start + mov(LOOPINC, LOOPCOUNT); + shr(LOOPINC, 12); + and(LOOPINC, 0xFF0); // Z-component is the incrementer + movzx(LOOPCOUNT, LOOPCOUNT.cvt8()); // X-component is iteration count + add(LOOPCOUNT, 1); // Iteration count is X-component + 1 + + Label l_loop_start; + L(l_loop_start); Compile_Block(instr.flow_control.dest_offset + 1); - ADD(32, R(LOOPCOUNT_REG), R(LOOPINC)); // Increment LOOPCOUNT_REG by Z-component - SUB(32, R(LOOPCOUNT), Imm8(1)); // Increment loop count by 1 - J_CC(CC_NZ, loop_start); // Loop if not equal + add(LOOPCOUNT_REG, LOOPINC); // Increment LOOPCOUNT_REG by Z-component + sub(LOOPCOUNT, 1); // Increment loop count by 1 + jnz(l_loop_start); // Loop if not equal looping = false; } @@ -755,8 +763,12 @@ void JitShader::Compile_JMP(Instruction instr) { bool inverted_condition = (instr.opcode.Value() == OpCode::Id::JMPU) && (instr.flow_control.num_instructions & 1); - FixupBranch b = J_CC(inverted_condition ? CC_Z : CC_NZ, true); - fixup_branches.push_back({b, instr.flow_control.dest_offset}); + Label& b = instruction_labels[instr.flow_control.dest_offset]; + if (inverted_condition) { + jz(b, T_NEAR); + } else { + jnz(b, T_NEAR); + } } void JitShader::Compile_Block(unsigned end) { @@ -767,13 +779,14 @@ void JitShader::Compile_Block(unsigned end) { void JitShader::Compile_Return() { // Peek return offset on the stack and check if we're at that offset - MOV(64, R(RAX), MDisp(RSP, 8)); - CMP(32, R(RAX), Imm32(program_counter)); + mov(rax, qword[rsp + 8]); + cmp(eax, (program_counter)); // If so, jump back to before CALL - FixupBranch b = J_CC(CC_NZ, true); - RET(); - SetJumpTarget(b); + Label b; + jnz(b); + ret(); + L(b); } void JitShader::Compile_NextInstr() { @@ -781,9 +794,7 @@ void JitShader::Compile_NextInstr() { Compile_Return(); } - ASSERT_MSG(code_ptr[program_counter] == nullptr, - "Tried to compile already compiled shader location!"); - code_ptr[program_counter] = GetCodePtr(); + L(instruction_labels[program_counter]); Instruction instr = GetVertexShaderInstruction(program_counter++); @@ -824,64 +835,53 @@ void JitShader::FindReturnOffsets() { void JitShader::Compile() { // Reset flow control state - program = (CompiledShader*)GetCodePtr(); + program = (CompiledShader*)getCurr(); program_counter = 0; looping = false; - code_ptr.fill(nullptr); - fixup_branches.clear(); + instruction_labels.fill(Xbyak::Label()); // Find all `CALL` instructions and identify return locations FindReturnOffsets(); // The stack pointer is 8 modulo 16 at the entry of a procedure - ABI_PushRegistersAndAdjustStack(ABI_ALL_CALLEE_SAVED, 8); + ABI_PushRegistersAndAdjustStack(*this, ABI_ALL_CALLEE_SAVED, 8); - MOV(PTRBITS, R(SETUP), R(ABI_PARAM1)); - MOV(PTRBITS, R(STATE), R(ABI_PARAM2)); + mov(SETUP, ABI_PARAM1); + mov(STATE, ABI_PARAM2); // Zero address/loop registers - XOR(64, R(ADDROFFS_REG_0), R(ADDROFFS_REG_0)); - XOR(64, R(ADDROFFS_REG_1), R(ADDROFFS_REG_1)); - XOR(64, R(LOOPCOUNT_REG), R(LOOPCOUNT_REG)); + xor(ADDROFFS_REG_0.cvt32(), ADDROFFS_REG_0.cvt32()); + xor(ADDROFFS_REG_1.cvt32(), ADDROFFS_REG_1.cvt32()); + xor(LOOPCOUNT_REG, LOOPCOUNT_REG); // Used to set a register to one static const __m128 one = {1.f, 1.f, 1.f, 1.f}; - MOV(PTRBITS, R(RAX), ImmPtr(&one)); - MOVAPS(ONE, MatR(RAX)); + mov(rax, reinterpret_cast<size_t>(&one)); + movaps(ONE, xword[rax]); // Used to negate registers static const __m128 neg = {-0.f, -0.f, -0.f, -0.f}; - MOV(PTRBITS, R(RAX), ImmPtr(&neg)); - MOVAPS(NEGBIT, MatR(RAX)); + mov(rax, reinterpret_cast<size_t>(&neg)); + movaps(NEGBIT, xword[rax]); // Jump to start of the shader program - JMPptr(R(ABI_PARAM3)); + jmp(ABI_PARAM3); // Compile entire program Compile_Block(static_cast<unsigned>(g_state.vs.program_code.size())); - // Set the target for any incomplete branches now that the entire shader program has been - // emitted - for (const auto& branch : fixup_branches) { - SetJumpTarget(branch.first, code_ptr[branch.second]); - } - // Free memory that's no longer needed return_offsets.clear(); return_offsets.shrink_to_fit(); - fixup_branches.clear(); - fixup_branches.shrink_to_fit(); - uintptr_t size = - reinterpret_cast<uintptr_t>(GetCodePtr()) - reinterpret_cast<uintptr_t>(program); - ASSERT_MSG(size <= MAX_SHADER_SIZE, "Compiled a shader that exceeds the allocated size!"); + ready(); + uintptr_t size = reinterpret_cast<uintptr_t>(getCurr()) - reinterpret_cast<uintptr_t>(program); + ASSERT_MSG(size <= MAX_SHADER_SIZE, "Compiled a shader that exceeds the allocated size!"); LOG_DEBUG(HW_GPU, "Compiled shader size=%lu", size); } -JitShader::JitShader() { - AllocCodeSpace(MAX_SHADER_SIZE); -} +JitShader::JitShader() : Xbyak::CodeGenerator(MAX_SHADER_SIZE) {} } // namespace Shader diff --git a/src/video_core/shader/shader_jit_x64.h b/src/video_core/shader/shader_jit_x64.h index 98de5ecef..f37548306 100644 --- a/src/video_core/shader/shader_jit_x64.h +++ b/src/video_core/shader/shader_jit_x64.h @@ -9,6 +9,7 @@ #include <utility> #include <vector> #include <nihstro/shader_bytecode.h> +#include <xbyak.h> #include "common/bit_set.h" #include "common/common_types.h" #include "common/x64/emitter.h" @@ -29,12 +30,12 @@ constexpr size_t MAX_SHADER_SIZE = 1024 * 64; * This class implements the shader JIT compiler. It recompiles a Pica shader program into x86_64 * code that can be executed on the host machine directly. */ -class JitShader : public Gen::XCodeBlock { +class JitShader : public Xbyak::CodeGenerator { public: JitShader(); - void Run(const ShaderSetup& setup, UnitState<false>& state, unsigned offset) const { - program(&setup, &state, code_ptr[offset]); + void Run(const ShaderSetup& setup, UnitState& state, unsigned offset) const { + program(&setup, &state, instruction_labels[offset].getAddress()); } void Compile(); @@ -71,14 +72,14 @@ private: void Compile_NextInstr(); void Compile_SwizzleSrc(Instruction instr, unsigned src_num, SourceRegister src_reg, - Gen::X64Reg dest); - void Compile_DestEnable(Instruction instr, Gen::X64Reg dest); + Xbyak::Xmm dest); + void Compile_DestEnable(Instruction instr, Xbyak::Xmm dest); /** * Compiles a `MUL src1, src2` operation, properly handling the PICA semantics when multiplying * zero by inf. Clobbers `src2` and `scratch`. */ - void Compile_SanitizedMul(Gen::X64Reg src1, Gen::X64Reg src2, Gen::X64Reg scratch); + void Compile_SanitizedMul(Xbyak::Xmm src1, Xbyak::Xmm src2, Xbyak::Xmm scratch); void Compile_EvaluateCondition(Instruction instr); void Compile_UniformCondition(Instruction instr); @@ -103,7 +104,7 @@ private: void FindReturnOffsets(); /// Mapping of Pica VS instructions to pointers in the emitted code - std::array<const u8*, 1024> code_ptr; + std::array<Xbyak::Label, 1024> instruction_labels; /// Offsets in code where a return needs to be inserted std::vector<unsigned> return_offsets; @@ -111,9 +112,6 @@ private: unsigned program_counter = 0; ///< Offset of the next instruction to decode bool looping = false; ///< True if compiling a loop, used to check for nested loops - /// Branches that need to be fixed up once the entire shader program is compiled - std::vector<std::pair<Gen::FixupBranch, unsigned>> fixup_branches; - using CompiledShader = void(const void* setup, void* state, const u8* start_addr); CompiledShader* program = nullptr; }; |
