diff options
Diffstat (limited to 'src/video_core/renderer_opengl')
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.cpp | 1686 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.h | 316 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | 799 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer_cache.h | 239 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_shader_gen.cpp | 1231 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_shader_gen.h | 162 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/pica_to_gl.h | 235 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.cpp | 7 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/renderer_opengl.h | 1 |
9 files changed, 0 insertions, 4676 deletions
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp deleted file mode 100644 index becaf7bde..000000000 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ /dev/null @@ -1,1686 +0,0 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <memory> -#include <string> -#include <tuple> -#include <utility> -#include <glad/glad.h> -#include "common/assert.h" -#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_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/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)); - -RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) { - // Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0 - state.clip_distance[0] = true; - - // Create sampler objects - for (size_t i = 0; i < texture_samplers.size(); ++i) { - texture_samplers[i].Create(); - state.texture_units[i].sampler = texture_samplers[i].sampler.handle; - } - - // Generate VBO, VAO and UBO - vertex_buffer.Create(); - vertex_array.Create(); - uniform_buffer.Create(); - - state.draw.vertex_array = vertex_array.handle; - state.draw.vertex_buffer = vertex_buffer.handle; - state.draw.uniform_buffer = uniform_buffer.handle; - state.Apply(); - - // Bind the UBO to binding point 0 - glBindBufferBase(GL_UNIFORM_BUFFER, 0, uniform_buffer.handle); - - uniform_block_data.dirty = true; - - uniform_block_data.lut_dirty.fill(true); - - uniform_block_data.fog_lut_dirty = true; - - uniform_block_data.proctex_noise_lut_dirty = true; - uniform_block_data.proctex_color_map_dirty = true; - uniform_block_data.proctex_alpha_map_dirty = true; - uniform_block_data.proctex_lut_dirty = true; - uniform_block_data.proctex_diff_lut_dirty = true; - - // Set vertex attributes - glVertexAttribPointer(GLShader::ATTRIBUTE_POSITION, 4, GL_FLOAT, GL_FALSE, - sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, position)); - glEnableVertexAttribArray(GLShader::ATTRIBUTE_POSITION); - - glVertexAttribPointer(GLShader::ATTRIBUTE_COLOR, 4, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), - (GLvoid*)offsetof(HardwareVertex, color)); - glEnableVertexAttribArray(GLShader::ATTRIBUTE_COLOR); - - glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORD0, 2, GL_FLOAT, GL_FALSE, - sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord0)); - glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORD1, 2, GL_FLOAT, GL_FALSE, - sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord1)); - glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORD2, 2, GL_FLOAT, GL_FALSE, - sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord2)); - glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD0); - glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD1); - glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD2); - - glVertexAttribPointer(GLShader::ATTRIBUTE_TEXCOORD0_W, 1, GL_FLOAT, GL_FALSE, - sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, tex_coord0_w)); - glEnableVertexAttribArray(GLShader::ATTRIBUTE_TEXCOORD0_W); - - glVertexAttribPointer(GLShader::ATTRIBUTE_NORMQUAT, 4, GL_FLOAT, GL_FALSE, - sizeof(HardwareVertex), (GLvoid*)offsetof(HardwareVertex, normquat)); - glEnableVertexAttribArray(GLShader::ATTRIBUTE_NORMQUAT); - - glVertexAttribPointer(GLShader::ATTRIBUTE_VIEW, 3, GL_FLOAT, GL_FALSE, sizeof(HardwareVertex), - (GLvoid*)offsetof(HardwareVertex, view)); - glEnableVertexAttribArray(GLShader::ATTRIBUTE_VIEW); - - // Create render framebuffer - framebuffer.Create(); - - // Allocate and bind lighting lut textures - lighting_lut.Create(); - state.lighting_lut.texture_buffer = lighting_lut.handle; - state.Apply(); - lighting_lut_buffer.Create(); - glBindBuffer(GL_TEXTURE_BUFFER, lighting_lut_buffer.handle); - glBufferData(GL_TEXTURE_BUFFER, - sizeof(GLfloat) * 2 * 256 * Pica::LightingRegs::NumLightingSampler, nullptr, - GL_DYNAMIC_DRAW); - glActiveTexture(TextureUnits::LightingLUT.Enum()); - glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32F, lighting_lut_buffer.handle); - - // Setup the LUT for the fog - fog_lut.Create(); - state.fog_lut.texture_buffer = fog_lut.handle; - state.Apply(); - fog_lut_buffer.Create(); - glBindBuffer(GL_TEXTURE_BUFFER, fog_lut_buffer.handle); - glBufferData(GL_TEXTURE_BUFFER, sizeof(GLfloat) * 2 * 128, nullptr, GL_DYNAMIC_DRAW); - glActiveTexture(TextureUnits::FogLUT.Enum()); - glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32F, fog_lut_buffer.handle); - - // Setup the noise LUT for proctex - proctex_noise_lut.Create(); - state.proctex_noise_lut.texture_buffer = proctex_noise_lut.handle; - state.Apply(); - proctex_noise_lut_buffer.Create(); - glBindBuffer(GL_TEXTURE_BUFFER, proctex_noise_lut_buffer.handle); - glBufferData(GL_TEXTURE_BUFFER, sizeof(GLfloat) * 2 * 128, nullptr, GL_DYNAMIC_DRAW); - glActiveTexture(TextureUnits::ProcTexNoiseLUT.Enum()); - glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32F, proctex_noise_lut_buffer.handle); - - // Setup the color map for proctex - proctex_color_map.Create(); - state.proctex_color_map.texture_buffer = proctex_color_map.handle; - state.Apply(); - proctex_color_map_buffer.Create(); - glBindBuffer(GL_TEXTURE_BUFFER, proctex_color_map_buffer.handle); - glBufferData(GL_TEXTURE_BUFFER, sizeof(GLfloat) * 2 * 128, nullptr, GL_DYNAMIC_DRAW); - glActiveTexture(TextureUnits::ProcTexColorMap.Enum()); - glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32F, proctex_color_map_buffer.handle); - - // Setup the alpha map for proctex - proctex_alpha_map.Create(); - state.proctex_alpha_map.texture_buffer = proctex_alpha_map.handle; - state.Apply(); - proctex_alpha_map_buffer.Create(); - glBindBuffer(GL_TEXTURE_BUFFER, proctex_alpha_map_buffer.handle); - glBufferData(GL_TEXTURE_BUFFER, sizeof(GLfloat) * 2 * 128, nullptr, GL_DYNAMIC_DRAW); - glActiveTexture(TextureUnits::ProcTexAlphaMap.Enum()); - glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32F, proctex_alpha_map_buffer.handle); - - // Setup the LUT for proctex - proctex_lut.Create(); - state.proctex_lut.texture_buffer = proctex_lut.handle; - state.Apply(); - proctex_lut_buffer.Create(); - glBindBuffer(GL_TEXTURE_BUFFER, proctex_lut_buffer.handle); - glBufferData(GL_TEXTURE_BUFFER, sizeof(GLfloat) * 4 * 256, nullptr, GL_DYNAMIC_DRAW); - glActiveTexture(TextureUnits::ProcTexLUT.Enum()); - glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, proctex_lut_buffer.handle); - - // Setup the difference LUT for proctex - proctex_diff_lut.Create(); - state.proctex_diff_lut.texture_buffer = proctex_diff_lut.handle; - state.Apply(); - proctex_diff_lut_buffer.Create(); - glBindBuffer(GL_TEXTURE_BUFFER, proctex_diff_lut_buffer.handle); - glBufferData(GL_TEXTURE_BUFFER, sizeof(GLfloat) * 4 * 256, nullptr, GL_DYNAMIC_DRAW); - glActiveTexture(TextureUnits::ProcTexDiffLUT.Enum()); - glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, proctex_diff_lut_buffer.handle); - - // Sync fixed function OpenGL state - SyncClipEnabled(); - SyncClipCoef(); - SyncCullMode(); - SyncBlendEnabled(); - SyncBlendFuncs(); - SyncBlendColor(); - SyncLogicOp(); - SyncStencilTest(); - SyncDepthTest(); - SyncColorWriteMask(); - SyncStencilWriteMask(); - SyncDepthWriteMask(); -} - -RasterizerOpenGL::~RasterizerOpenGL() {} - -/** - * This is a helper function to resolve an issue when interpolating opposite quaternions. See below - * for a detailed description of this issue (yuriks): - * - * For any rotation, there are two quaternions Q, and -Q, that represent the same rotation. If you - * interpolate two quaternions that are opposite, instead of going from one rotation to another - * using the shortest path, you'll go around the longest path. You can test if two quaternions are - * opposite by checking if Dot(Q1, Q2) < 0. In that case, you can flip either of them, therefore - * making Dot(Q1, -Q2) positive. - * - * This solution corrects this issue per-vertex before passing the quaternions to OpenGL. This is - * correct for most cases but can still rotate around the long way sometimes. An implementation - * which did `lerp(lerp(Q1, Q2), Q3)` (with proper weighting), applying the dot product check - * between each step would work for those cases at the cost of being more complex to implement. - * - * Fortunately however, the 3DS hardware happens to also use this exact same logic to work around - * these issues, making this basic implementation actually more accurate to the hardware. - */ -static bool AreQuaternionsOpposite(Math::Vec4<Pica::float24> qa, Math::Vec4<Pica::float24> qb) { - Math::Vec4f a{qa.x.ToFloat32(), qa.y.ToFloat32(), qa.z.ToFloat32(), qa.w.ToFloat32()}; - Math::Vec4f b{qb.x.ToFloat32(), qb.y.ToFloat32(), qb.z.ToFloat32(), qb.w.ToFloat32()}; - - return (Math::Dot(a, b) < 0.f); -} - -void RasterizerOpenGL::AddTriangle(const Pica::Shader::OutputVertex& v0, - const Pica::Shader::OutputVertex& v1, - const Pica::Shader::OutputVertex& v2) { - vertex_batch.emplace_back(v0, false); - vertex_batch.emplace_back(v1, AreQuaternionsOpposite(v0.quat, v1.quat)); - vertex_batch.emplace_back(v2, AreQuaternionsOpposite(v0.quat, v2.quat)); -} - -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 - CachedSurface* color_surface; - CachedSurface* depth_surface; - MathUtil::Rectangle<int> rect; - std::tie(color_surface, depth_surface, rect) = - res_cache.GetFramebufferSurfaces(regs.framebuffer.framebuffer); - - state.draw.draw_framebuffer = framebuffer.handle; - state.Apply(); - - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - color_surface != nullptr ? color_surface->texture.handle : 0, 0); - if (depth_surface != nullptr) { - if (regs.framebuffer.framebuffer.depth_format == - Pica::FramebufferRegs::DepthFormat::D24S8) { - // attach both depth and stencil - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, - depth_surface->texture.handle, 0); - } else { - // attach depth - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, - depth_surface->texture.handle, 0); - // clear stencil attachment - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); - } - } else { - // clear both depth and stencil attachment - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 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.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.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) { - - uniform_block_data.data.framebuffer_scale[0] = color_surface->res_scale_width; - uniform_block_data.data.framebuffer_scale[1] = color_surface->res_scale_height; - uniform_block_data.dirty = true; - } - - // 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.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.rasterizer.scissor_test.x2 + 1) * color_surface->res_scale_width); - GLint scissor_y2 = static_cast<GLint>( - 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 || - uniform_block_data.data.scissor_y1 != scissor_y1 || - uniform_block_data.data.scissor_y2 != scissor_y2) { - - uniform_block_data.data.scissor_x1 = scissor_x1; - uniform_block_data.data.scissor_x2 = scissor_x2; - uniform_block_data.data.scissor_y1 = scissor_y1; - uniform_block_data.data.scissor_y2 = scissor_y2; - uniform_block_data.dirty = true; - } - - // Sync and bind the texture surfaces - 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]; - - if (texture.enabled) { - texture_samplers[texture_index].SyncWithConfig(texture.config); - CachedSurface* surface = res_cache.GetTextureSurface(texture); - if (surface != nullptr) { - state.texture_units[texture_index].texture_2d = surface->texture.handle; - } else { - // Can occur when texture addr is null or its memory is unmapped/invalid - state.texture_units[texture_index].texture_2d = 0; - } - } else { - state.texture_units[texture_index].texture_2d = 0; - } - } - - // Sync and bind the shader - if (shader_dirty) { - SetShader(); - shader_dirty = false; - } - - // Sync the lighting luts - for (unsigned index = 0; index < uniform_block_data.lut_dirty.size(); index++) { - if (uniform_block_data.lut_dirty[index]) { - SyncLightingLUT(index); - uniform_block_data.lut_dirty[index] = false; - } - } - - // Sync the fog lut - if (uniform_block_data.fog_lut_dirty) { - SyncFogLUT(); - uniform_block_data.fog_lut_dirty = false; - } - - // Sync the proctex noise lut - if (uniform_block_data.proctex_noise_lut_dirty) { - SyncProcTexNoiseLUT(); - uniform_block_data.proctex_noise_lut_dirty = false; - } - - // Sync the proctex color map - if (uniform_block_data.proctex_color_map_dirty) { - SyncProcTexColorMap(); - uniform_block_data.proctex_color_map_dirty = false; - } - - // Sync the proctex alpha map - if (uniform_block_data.proctex_alpha_map_dirty) { - SyncProcTexAlphaMap(); - uniform_block_data.proctex_alpha_map_dirty = false; - } - - // Sync the proctex lut - if (uniform_block_data.proctex_lut_dirty) { - SyncProcTexLUT(); - uniform_block_data.proctex_lut_dirty = false; - } - - // Sync the proctex difference lut - if (uniform_block_data.proctex_diff_lut_dirty) { - SyncProcTexDiffLUT(); - uniform_block_data.proctex_diff_lut_dirty = false; - } - - // Sync the uniform data - if (uniform_block_data.dirty) { - glBufferData(GL_UNIFORM_BUFFER, sizeof(UniformData), &uniform_block_data.data, - GL_STATIC_DRAW); - uniform_block_data.dirty = false; - } - - state.Apply(); - - // Draw the vertex batch - glBufferData(GL_ARRAY_BUFFER, vertex_batch.size() * sizeof(HardwareVertex), vertex_batch.data(), - GL_STREAM_DRAW); - glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertex_batch.size()); - - // Mark framebuffer surfaces as dirty - // TODO: Restrict invalidation area to the viewport - if (color_surface != nullptr) { - color_surface->dirty = true; - res_cache.FlushRegion(color_surface->addr, color_surface->size, color_surface, true); - } - if (depth_surface != nullptr) { - depth_surface->dirty = true; - res_cache.FlushRegion(depth_surface->addr, depth_surface->size, depth_surface, true); - } - - vertex_batch.clear(); - - // Unbind textures for potential future use as framebuffer attachments - for (unsigned texture_index = 0; texture_index < pica_textures.size(); ++texture_index) { - state.texture_units[texture_index].texture_2d = 0; - } - state.Apply(); -} - -void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { - const auto& regs = Pica::g_state.regs; - - switch (id) { - // Culling - case PICA_REG_INDEX(rasterizer.cull_mode): - SyncCullMode(); - break; - - // Clipping plane - case PICA_REG_INDEX(rasterizer.clip_enable): - SyncClipEnabled(); - break; - - case PICA_REG_INDEX_WORKAROUND(rasterizer.clip_coef[0], 0x48): - case PICA_REG_INDEX_WORKAROUND(rasterizer.clip_coef[1], 0x49): - case PICA_REG_INDEX_WORKAROUND(rasterizer.clip_coef[2], 0x4a): - case PICA_REG_INDEX_WORKAROUND(rasterizer.clip_coef[3], 0x4b): - SyncClipCoef(); - break; - - // Depth modifiers - case PICA_REG_INDEX(rasterizer.viewport_depth_range): - SyncDepthScale(); - break; - case PICA_REG_INDEX(rasterizer.viewport_depth_near_plane): - SyncDepthOffset(); - break; - - // Depth buffering - case PICA_REG_INDEX(rasterizer.depthmap_enable): - shader_dirty = true; - break; - - // Blending - case PICA_REG_INDEX(framebuffer.output_merger.alphablend_enable): - SyncBlendEnabled(); - break; - case PICA_REG_INDEX(framebuffer.output_merger.alpha_blending): - SyncBlendFuncs(); - break; - case PICA_REG_INDEX(framebuffer.output_merger.blend_const): - SyncBlendColor(); - break; - - // Fog state - case PICA_REG_INDEX(texturing.fog_color): - SyncFogColor(); - break; - 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; - - // ProcTex state - case PICA_REG_INDEX(texturing.proctex): - case PICA_REG_INDEX(texturing.proctex_lut): - case PICA_REG_INDEX(texturing.proctex_lut_offset): - shader_dirty = true; - break; - - case PICA_REG_INDEX(texturing.proctex_noise_u): - case PICA_REG_INDEX(texturing.proctex_noise_v): - case PICA_REG_INDEX(texturing.proctex_noise_frequency): - SyncProcTexNoise(); - break; - - case PICA_REG_INDEX_WORKAROUND(texturing.proctex_lut_data[0], 0xb0): - case PICA_REG_INDEX_WORKAROUND(texturing.proctex_lut_data[1], 0xb1): - case PICA_REG_INDEX_WORKAROUND(texturing.proctex_lut_data[2], 0xb2): - case PICA_REG_INDEX_WORKAROUND(texturing.proctex_lut_data[3], 0xb3): - case PICA_REG_INDEX_WORKAROUND(texturing.proctex_lut_data[4], 0xb4): - case PICA_REG_INDEX_WORKAROUND(texturing.proctex_lut_data[5], 0xb5): - case PICA_REG_INDEX_WORKAROUND(texturing.proctex_lut_data[6], 0xb6): - case PICA_REG_INDEX_WORKAROUND(texturing.proctex_lut_data[7], 0xb7): - using Pica::TexturingRegs; - switch (regs.texturing.proctex_lut_config.ref_table.Value()) { - case TexturingRegs::ProcTexLutTable::Noise: - uniform_block_data.proctex_noise_lut_dirty = true; - break; - case TexturingRegs::ProcTexLutTable::ColorMap: - uniform_block_data.proctex_color_map_dirty = true; - break; - case TexturingRegs::ProcTexLutTable::AlphaMap: - uniform_block_data.proctex_alpha_map_dirty = true; - break; - case TexturingRegs::ProcTexLutTable::Color: - uniform_block_data.proctex_lut_dirty = true; - break; - case TexturingRegs::ProcTexLutTable::ColorDiff: - uniform_block_data.proctex_diff_lut_dirty = true; - break; - } - break; - - // 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(framebuffer.output_merger.stencil_test.raw_func): - SyncStencilTest(); - SyncStencilWriteMask(); - break; - 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(framebuffer.output_merger.depth_test_enable): - SyncDepthTest(); - SyncDepthWriteMask(); - SyncColorWriteMask(); - break; - - // Sync GL depth and stencil write mask - // (This is a dedicated combined depth / stencil write-enable register) - 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.framebuffer.allow_color_write): - SyncColorWriteMask(); - break; - - // Scissor test - case PICA_REG_INDEX(rasterizer.scissor_test.mode): - shader_dirty = true; - break; - - // Logic op - case PICA_REG_INDEX(framebuffer.output_merger.logic_op): - SyncLogicOp(); - break; - - case PICA_REG_INDEX(texturing.main_config): - shader_dirty = true; - break; - - // Texture 0 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(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(texturing.tev_stage0.const_r): - SyncTevConstColor(0, regs.texturing.tev_stage0); - break; - case PICA_REG_INDEX(texturing.tev_stage1.const_r): - SyncTevConstColor(1, regs.texturing.tev_stage1); - break; - case PICA_REG_INDEX(texturing.tev_stage2.const_r): - SyncTevConstColor(2, regs.texturing.tev_stage2); - break; - case PICA_REG_INDEX(texturing.tev_stage3.const_r): - SyncTevConstColor(3, regs.texturing.tev_stage3); - break; - case PICA_REG_INDEX(texturing.tev_stage4.const_r): - SyncTevConstColor(4, regs.texturing.tev_stage4); - break; - case PICA_REG_INDEX(texturing.tev_stage5.const_r): - SyncTevConstColor(5, regs.texturing.tev_stage5); - break; - - // 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.max_light_index): - case PICA_REG_INDEX(lighting.config0): - case PICA_REG_INDEX(lighting.config1): - case PICA_REG_INDEX(lighting.abs_lut_input): - case PICA_REG_INDEX(lighting.lut_input): - case PICA_REG_INDEX(lighting.lut_scale): - case PICA_REG_INDEX(lighting.light_enable): - break; - - // Fragment lighting specular 0 color - case PICA_REG_INDEX_WORKAROUND(lighting.light[0].specular_0, 0x140 + 0 * 0x10): - SyncLightSpecular0(0); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[1].specular_0, 0x140 + 1 * 0x10): - SyncLightSpecular0(1); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[2].specular_0, 0x140 + 2 * 0x10): - SyncLightSpecular0(2); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[3].specular_0, 0x140 + 3 * 0x10): - SyncLightSpecular0(3); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[4].specular_0, 0x140 + 4 * 0x10): - SyncLightSpecular0(4); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[5].specular_0, 0x140 + 5 * 0x10): - SyncLightSpecular0(5); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[6].specular_0, 0x140 + 6 * 0x10): - SyncLightSpecular0(6); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[7].specular_0, 0x140 + 7 * 0x10): - SyncLightSpecular0(7); - break; - - // Fragment lighting specular 1 color - case PICA_REG_INDEX_WORKAROUND(lighting.light[0].specular_1, 0x141 + 0 * 0x10): - SyncLightSpecular1(0); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[1].specular_1, 0x141 + 1 * 0x10): - SyncLightSpecular1(1); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[2].specular_1, 0x141 + 2 * 0x10): - SyncLightSpecular1(2); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[3].specular_1, 0x141 + 3 * 0x10): - SyncLightSpecular1(3); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[4].specular_1, 0x141 + 4 * 0x10): - SyncLightSpecular1(4); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[5].specular_1, 0x141 + 5 * 0x10): - SyncLightSpecular1(5); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[6].specular_1, 0x141 + 6 * 0x10): - SyncLightSpecular1(6); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[7].specular_1, 0x141 + 7 * 0x10): - SyncLightSpecular1(7); - break; - - // Fragment lighting diffuse color - case PICA_REG_INDEX_WORKAROUND(lighting.light[0].diffuse, 0x142 + 0 * 0x10): - SyncLightDiffuse(0); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[1].diffuse, 0x142 + 1 * 0x10): - SyncLightDiffuse(1); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[2].diffuse, 0x142 + 2 * 0x10): - SyncLightDiffuse(2); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[3].diffuse, 0x142 + 3 * 0x10): - SyncLightDiffuse(3); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[4].diffuse, 0x142 + 4 * 0x10): - SyncLightDiffuse(4); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[5].diffuse, 0x142 + 5 * 0x10): - SyncLightDiffuse(5); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[6].diffuse, 0x142 + 6 * 0x10): - SyncLightDiffuse(6); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[7].diffuse, 0x142 + 7 * 0x10): - SyncLightDiffuse(7); - break; - - // Fragment lighting ambient color - case PICA_REG_INDEX_WORKAROUND(lighting.light[0].ambient, 0x143 + 0 * 0x10): - SyncLightAmbient(0); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[1].ambient, 0x143 + 1 * 0x10): - SyncLightAmbient(1); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[2].ambient, 0x143 + 2 * 0x10): - SyncLightAmbient(2); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[3].ambient, 0x143 + 3 * 0x10): - SyncLightAmbient(3); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[4].ambient, 0x143 + 4 * 0x10): - SyncLightAmbient(4); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[5].ambient, 0x143 + 5 * 0x10): - SyncLightAmbient(5); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[6].ambient, 0x143 + 6 * 0x10): - SyncLightAmbient(6); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[7].ambient, 0x143 + 7 * 0x10): - SyncLightAmbient(7); - break; - - // Fragment lighting position - case PICA_REG_INDEX_WORKAROUND(lighting.light[0].x, 0x144 + 0 * 0x10): - case PICA_REG_INDEX_WORKAROUND(lighting.light[0].z, 0x145 + 0 * 0x10): - SyncLightPosition(0); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[1].x, 0x144 + 1 * 0x10): - case PICA_REG_INDEX_WORKAROUND(lighting.light[1].z, 0x145 + 1 * 0x10): - SyncLightPosition(1); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[2].x, 0x144 + 2 * 0x10): - case PICA_REG_INDEX_WORKAROUND(lighting.light[2].z, 0x145 + 2 * 0x10): - SyncLightPosition(2); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[3].x, 0x144 + 3 * 0x10): - case PICA_REG_INDEX_WORKAROUND(lighting.light[3].z, 0x145 + 3 * 0x10): - SyncLightPosition(3); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[4].x, 0x144 + 4 * 0x10): - case PICA_REG_INDEX_WORKAROUND(lighting.light[4].z, 0x145 + 4 * 0x10): - SyncLightPosition(4); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[5].x, 0x144 + 5 * 0x10): - case PICA_REG_INDEX_WORKAROUND(lighting.light[5].z, 0x145 + 5 * 0x10): - SyncLightPosition(5); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[6].x, 0x144 + 6 * 0x10): - case PICA_REG_INDEX_WORKAROUND(lighting.light[6].z, 0x145 + 6 * 0x10): - SyncLightPosition(6); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[7].x, 0x144 + 7 * 0x10): - case PICA_REG_INDEX_WORKAROUND(lighting.light[7].z, 0x145 + 7 * 0x10): - SyncLightPosition(7); - break; - - // Fragment spot lighting direction - case PICA_REG_INDEX_WORKAROUND(lighting.light[0].spot_x, 0x146 + 0 * 0x10): - case PICA_REG_INDEX_WORKAROUND(lighting.light[0].spot_z, 0x147 + 0 * 0x10): - SyncLightSpotDirection(0); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[1].spot_x, 0x146 + 1 * 0x10): - case PICA_REG_INDEX_WORKAROUND(lighting.light[1].spot_z, 0x147 + 1 * 0x10): - SyncLightSpotDirection(1); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[2].spot_x, 0x146 + 2 * 0x10): - case PICA_REG_INDEX_WORKAROUND(lighting.light[2].spot_z, 0x147 + 2 * 0x10): - SyncLightSpotDirection(2); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[3].spot_x, 0x146 + 3 * 0x10): - case PICA_REG_INDEX_WORKAROUND(lighting.light[3].spot_z, 0x147 + 3 * 0x10): - SyncLightSpotDirection(3); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[4].spot_x, 0x146 + 4 * 0x10): - case PICA_REG_INDEX_WORKAROUND(lighting.light[4].spot_z, 0x147 + 4 * 0x10): - SyncLightSpotDirection(4); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[5].spot_x, 0x146 + 5 * 0x10): - case PICA_REG_INDEX_WORKAROUND(lighting.light[5].spot_z, 0x147 + 5 * 0x10): - SyncLightSpotDirection(5); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[6].spot_x, 0x146 + 6 * 0x10): - case PICA_REG_INDEX_WORKAROUND(lighting.light[6].spot_z, 0x147 + 6 * 0x10): - SyncLightSpotDirection(6); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[7].spot_x, 0x146 + 7 * 0x10): - case PICA_REG_INDEX_WORKAROUND(lighting.light[7].spot_z, 0x147 + 7 * 0x10): - SyncLightSpotDirection(7); - break; - - // Fragment lighting light source config - case PICA_REG_INDEX_WORKAROUND(lighting.light[0].config, 0x149 + 0 * 0x10): - case PICA_REG_INDEX_WORKAROUND(lighting.light[1].config, 0x149 + 1 * 0x10): - case PICA_REG_INDEX_WORKAROUND(lighting.light[2].config, 0x149 + 2 * 0x10): - case PICA_REG_INDEX_WORKAROUND(lighting.light[3].config, 0x149 + 3 * 0x10): - case PICA_REG_INDEX_WORKAROUND(lighting.light[4].config, 0x149 + 4 * 0x10): - case PICA_REG_INDEX_WORKAROUND(lighting.light[5].config, 0x149 + 5 * 0x10): - case PICA_REG_INDEX_WORKAROUND(lighting.light[6].config, 0x149 + 6 * 0x10): - case PICA_REG_INDEX_WORKAROUND(lighting.light[7].config, 0x149 + 7 * 0x10): - shader_dirty = true; - break; - - // Fragment lighting distance attenuation bias - case PICA_REG_INDEX_WORKAROUND(lighting.light[0].dist_atten_bias, 0x014A + 0 * 0x10): - SyncLightDistanceAttenuationBias(0); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[1].dist_atten_bias, 0x014A + 1 * 0x10): - SyncLightDistanceAttenuationBias(1); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[2].dist_atten_bias, 0x014A + 2 * 0x10): - SyncLightDistanceAttenuationBias(2); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[3].dist_atten_bias, 0x014A + 3 * 0x10): - SyncLightDistanceAttenuationBias(3); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[4].dist_atten_bias, 0x014A + 4 * 0x10): - SyncLightDistanceAttenuationBias(4); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[5].dist_atten_bias, 0x014A + 5 * 0x10): - SyncLightDistanceAttenuationBias(5); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[6].dist_atten_bias, 0x014A + 6 * 0x10): - SyncLightDistanceAttenuationBias(6); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[7].dist_atten_bias, 0x014A + 7 * 0x10): - SyncLightDistanceAttenuationBias(7); - break; - - // Fragment lighting distance attenuation scale - case PICA_REG_INDEX_WORKAROUND(lighting.light[0].dist_atten_scale, 0x014B + 0 * 0x10): - SyncLightDistanceAttenuationScale(0); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[1].dist_atten_scale, 0x014B + 1 * 0x10): - SyncLightDistanceAttenuationScale(1); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[2].dist_atten_scale, 0x014B + 2 * 0x10): - SyncLightDistanceAttenuationScale(2); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[3].dist_atten_scale, 0x014B + 3 * 0x10): - SyncLightDistanceAttenuationScale(3); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[4].dist_atten_scale, 0x014B + 4 * 0x10): - SyncLightDistanceAttenuationScale(4); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[5].dist_atten_scale, 0x014B + 5 * 0x10): - SyncLightDistanceAttenuationScale(5); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[6].dist_atten_scale, 0x014B + 6 * 0x10): - SyncLightDistanceAttenuationScale(6); - break; - case PICA_REG_INDEX_WORKAROUND(lighting.light[7].dist_atten_scale, 0x014B + 7 * 0x10): - SyncLightDistanceAttenuationScale(7); - break; - - // Fragment lighting global ambient color (emission + ambient * ambient) - case PICA_REG_INDEX_WORKAROUND(lighting.global_ambient, 0x1c0): - SyncGlobalAmbient(); - break; - - // Fragment lighting lookup tables - case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[0], 0x1c8): - case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[1], 0x1c9): - case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[2], 0x1ca): - case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[3], 0x1cb): - case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[4], 0x1cc): - case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[5], 0x1cd): - case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[6], 0x1ce): - case PICA_REG_INDEX_WORKAROUND(lighting.lut_data[7], 0x1cf): { - auto& lut_config = regs.lighting.lut_config; - uniform_block_data.lut_dirty[lut_config.type] = true; - break; - } - } -} - -void RasterizerOpenGL::FlushAll() { - MICROPROFILE_SCOPE(OpenGL_CacheManagement); - res_cache.FlushAll(); -} - -void RasterizerOpenGL::FlushRegion(PAddr addr, u64 size) { - MICROPROFILE_SCOPE(OpenGL_CacheManagement); - res_cache.FlushRegion(addr, size, nullptr, false); -} - -void RasterizerOpenGL::FlushAndInvalidateRegion(PAddr addr, u64 size) { - MICROPROFILE_SCOPE(OpenGL_CacheManagement); - res_cache.FlushRegion(addr, size, nullptr, true); -} - -bool RasterizerOpenGL::AccelerateDisplayTransfer(const GPU::Regs::DisplayTransferConfig& config) { - MICROPROFILE_SCOPE(OpenGL_Blits); - - CachedSurface src_params; - src_params.addr = config.GetPhysicalInputAddress(); - // It's important to use the correct source input width to properly skip over parts of the input - // image which will be cropped from the output but still affect the stride of the input image. - src_params.width = config.input_width; - // Using the output's height is fine because we don't read or skip over the remaining part of - // the image, and it allows for smaller texture cache lookup rectangles. - src_params.height = config.output_height; - src_params.is_tiled = !config.input_linear; - src_params.pixel_format = CachedSurface::PixelFormatFromGPUPixelFormat(config.input_format); - - CachedSurface dst_params; - dst_params.addr = config.GetPhysicalOutputAddress(); - dst_params.width = - config.scaling != config.NoScale ? config.output_width / 2 : config.output_width.Value(); - dst_params.height = - config.scaling == config.ScaleXY ? config.output_height / 2 : config.output_height.Value(); - dst_params.is_tiled = config.input_linear != config.dont_swizzle; - dst_params.pixel_format = CachedSurface::PixelFormatFromGPUPixelFormat(config.output_format); - - MathUtil::Rectangle<int> src_rect; - CachedSurface* src_surface = res_cache.GetSurfaceRect(src_params, false, true, src_rect); - - if (src_surface == nullptr) { - return false; - } - - // Adjust the source rectangle to take into account parts of the input lines being cropped - if (config.input_width > config.output_width) { - src_rect.right -= static_cast<int>((config.input_width - config.output_width) * - src_surface->res_scale_width); - } - - // Require destination surface to have same resolution scale as source to preserve scaling - dst_params.res_scale_width = src_surface->res_scale_width; - dst_params.res_scale_height = src_surface->res_scale_height; - - MathUtil::Rectangle<int> dst_rect; - CachedSurface* dst_surface = res_cache.GetSurfaceRect(dst_params, true, false, dst_rect); - - if (dst_surface == nullptr) { - return false; - } - - // Don't accelerate if the src and dst surfaces are the same - if (src_surface == dst_surface) { - return false; - } - - if (config.flip_vertically) { - std::swap(dst_rect.top, dst_rect.bottom); - } - - if (!res_cache.TryBlitSurfaces(src_surface, src_rect, dst_surface, dst_rect)) { - return false; - } - - u32 dst_size = dst_params.width * dst_params.height * - CachedSurface::GetFormatBpp(dst_params.pixel_format) / 8; - dst_surface->dirty = true; - res_cache.FlushRegion(config.GetPhysicalOutputAddress(), dst_size, dst_surface, true); - return true; -} - -bool RasterizerOpenGL::AccelerateTextureCopy(const GPU::Regs::DisplayTransferConfig& config) { - // TODO(tfarley): Try to hardware accelerate this - return false; -} - -bool RasterizerOpenGL::AccelerateFill(const GPU::Regs::MemoryFillConfig& config) { - MICROPROFILE_SCOPE(OpenGL_Blits); - using PixelFormat = CachedSurface::PixelFormat; - using SurfaceType = CachedSurface::SurfaceType; - - CachedSurface* dst_surface = res_cache.TryGetFillSurface(config); - - if (dst_surface == nullptr) { - return false; - } - - OpenGLState cur_state = OpenGLState::GetCurState(); - - SurfaceType dst_type = CachedSurface::GetFormatType(dst_surface->pixel_format); - - GLuint old_fb = cur_state.draw.draw_framebuffer; - cur_state.draw.draw_framebuffer = framebuffer.handle; - // TODO: When scissor test is implemented, need to disable scissor test in cur_state here so - // Clear call isn't affected - cur_state.Apply(); - - if (dst_type == SurfaceType::Color || dst_type == SurfaceType::Texture) { - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - dst_surface->texture.handle, 0); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, - 0); - - 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 - // cases - // For instance, checking if fill value's bytes/bits repeat to allow filling - // I8/A8/I4/A4/... - // Currently only handles formats that are multiples of the fill value size - - if (config.fill_24bit) { - switch (dst_surface->pixel_format) { - case PixelFormat::RGB8: - color_values[0] = config.value_24bit_r / 255.0f; - color_values[1] = config.value_24bit_g / 255.0f; - color_values[2] = config.value_24bit_b / 255.0f; - break; - default: - return false; - } - } else if (config.fill_32bit) { - u32 value = config.value_32bit; - - switch (dst_surface->pixel_format) { - case PixelFormat::RGBA8: - color_values[0] = (value >> 24) / 255.0f; - color_values[1] = ((value >> 16) & 0xFF) / 255.0f; - color_values[2] = ((value >> 8) & 0xFF) / 255.0f; - color_values[3] = (value & 0xFF) / 255.0f; - break; - default: - return false; - } - } else { - u16 value_16bit = config.value_16bit.Value(); - Math::Vec4<u8> color; - - switch (dst_surface->pixel_format) { - case PixelFormat::RGBA8: - color_values[0] = (value_16bit >> 8) / 255.0f; - color_values[1] = (value_16bit & 0xFF) / 255.0f; - color_values[2] = color_values[0]; - color_values[3] = color_values[1]; - break; - case PixelFormat::RGB5A1: - color = Color::DecodeRGB5A1((const u8*)&value_16bit); - color_values[0] = color[0] / 31.0f; - color_values[1] = color[1] / 31.0f; - color_values[2] = color[2] / 31.0f; - color_values[3] = color[3]; - break; - case PixelFormat::RGB565: - color = Color::DecodeRGB565((const u8*)&value_16bit); - color_values[0] = color[0] / 31.0f; - color_values[1] = color[1] / 63.0f; - color_values[2] = color[2] / 31.0f; - break; - case PixelFormat::RGBA4: - color = Color::DecodeRGBA4((const u8*)&value_16bit); - color_values[0] = color[0] / 15.0f; - color_values[1] = color[1] / 15.0f; - color_values[2] = color[2] / 15.0f; - color_values[3] = color[3] / 15.0f; - break; - case PixelFormat::IA8: - case PixelFormat::RG8: - color_values[0] = (value_16bit >> 8) / 255.0f; - color_values[1] = (value_16bit & 0xFF) / 255.0f; - break; - default: - return false; - } - } - - 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) { - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, - dst_surface->texture.handle, 0); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); - - GLfloat value_float; - if (dst_surface->pixel_format == CachedSurface::PixelFormat::D16) { - value_float = config.value_32bit / 65535.0f; // 2^16 - 1 - } else if (dst_surface->pixel_format == CachedSurface::PixelFormat::D24) { - value_float = config.value_32bit / 16777215.0f; // 2^24 - 1 - } - - cur_state.depth.write_mask = GL_TRUE; - cur_state.Apply(); - glClearBufferfv(GL_DEPTH, 0, &value_float); - } else if (dst_type == SurfaceType::DepthStencil) { - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, - dst_surface->texture.handle, 0); - - GLfloat value_float = (config.value_32bit & 0xFFFFFF) / 16777215.0f; // 2^24 - 1 - GLint value_int = (config.value_32bit >> 24); - - cur_state.depth.write_mask = GL_TRUE; - cur_state.stencil.write_mask = 0xFF; - cur_state.Apply(); - glClearBufferfi(GL_DEPTH_STENCIL, 0, value_float, value_int); - } - - cur_state.draw.draw_framebuffer = old_fb; - // TODO: Return scissor test to previous value when scissor test is implemented - cur_state.Apply(); - - dst_surface->dirty = true; - res_cache.FlushRegion(dst_surface->addr, dst_surface->size, dst_surface, true); - return true; -} - -bool RasterizerOpenGL::AccelerateDisplay(const GPU::Regs::FramebufferConfig& config, - PAddr framebuffer_addr, u32 pixel_stride, - ScreenInfo& screen_info) { - if (framebuffer_addr == 0) { - return false; - } - MICROPROFILE_SCOPE(OpenGL_CacheManagement); - - CachedSurface src_params; - src_params.addr = framebuffer_addr; - src_params.width = config.width; - src_params.height = config.height; - src_params.pixel_stride = pixel_stride; - src_params.is_tiled = false; - src_params.pixel_format = CachedSurface::PixelFormatFromGPUPixelFormat(config.color_format); - - MathUtil::Rectangle<int> src_rect; - CachedSurface* src_surface = res_cache.GetSurfaceRect(src_params, false, true, src_rect); - - if (src_surface == nullptr) { - return false; - } - - u32 scaled_width = src_surface->GetScaledWidth(); - u32 scaled_height = src_surface->GetScaledHeight(); - - screen_info.display_texcoords = MathUtil::Rectangle<float>( - (float)src_rect.top / (float)scaled_height, (float)src_rect.left / (float)scaled_width, - (float)src_rect.bottom / (float)scaled_height, (float)src_rect.right / (float)scaled_width); - - screen_info.display_texture = src_surface->texture.handle; - - return true; -} - -void RasterizerOpenGL::SamplerInfo::Create() { - sampler.Create(); - mag_filter = min_filter = TextureConfig::Linear; - wrap_s = wrap_t = TextureConfig::Repeat; - border_color = 0; - - glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, - GL_LINEAR); // default is GL_LINEAR_MIPMAP_LINEAR - // Other attributes have correct defaults -} - -void RasterizerOpenGL::SamplerInfo::SyncWithConfig( - const Pica::TexturingRegs::TextureConfig& config) { - - GLuint s = sampler.handle; - - if (mag_filter != config.mag_filter) { - mag_filter = config.mag_filter; - glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, PicaToGL::TextureFilterMode(mag_filter)); - } - if (min_filter != config.min_filter) { - min_filter = config.min_filter; - glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, PicaToGL::TextureFilterMode(min_filter)); - } - - if (wrap_s != config.wrap_s) { - wrap_s = config.wrap_s; - glSamplerParameteri(s, GL_TEXTURE_WRAP_S, PicaToGL::WrapMode(wrap_s)); - } - if (wrap_t != config.wrap_t) { - wrap_t = config.wrap_t; - glSamplerParameteri(s, GL_TEXTURE_WRAP_T, PicaToGL::WrapMode(wrap_t)); - } - - if (wrap_s == TextureConfig::ClampToBorder || wrap_t == TextureConfig::ClampToBorder) { - if (border_color != config.border_color.raw) { - border_color = config.border_color.raw; - auto gl_color = PicaToGL::ColorRGBA8(border_color); - glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, gl_color.data()); - } - } -} - -void RasterizerOpenGL::SetShader() { - auto config = GLShader::PicaShaderConfig::BuildFromRegs(Pica::g_state.regs); - std::unique_ptr<PicaShader> shader = std::make_unique<PicaShader>(); - - // Find (or generate) the GLSL shader for the current TEV state - auto cached_shader = shader_cache.find(config); - if (cached_shader != shader_cache.end()) { - current_shader = cached_shader->second.get(); - - state.draw.shader_program = current_shader->shader.handle; - state.Apply(); - } else { - LOG_DEBUG(Render_OpenGL, "Creating new shader"); - - shader->shader.Create(GLShader::GenerateVertexShader().c_str(), - GLShader::GenerateFragmentShader(config).c_str()); - - state.draw.shader_program = shader->shader.handle; - state.Apply(); - - // Set the texture samplers to correspond to different texture units - GLint uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[0]"); - if (uniform_tex != -1) { - glUniform1i(uniform_tex, TextureUnits::PicaTexture(0).id); - } - uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[1]"); - if (uniform_tex != -1) { - glUniform1i(uniform_tex, TextureUnits::PicaTexture(1).id); - } - uniform_tex = glGetUniformLocation(shader->shader.handle, "tex[2]"); - if (uniform_tex != -1) { - glUniform1i(uniform_tex, TextureUnits::PicaTexture(2).id); - } - - // Set the texture samplers to correspond to different lookup table texture units - GLint uniform_lut = glGetUniformLocation(shader->shader.handle, "lighting_lut"); - if (uniform_lut != -1) { - glUniform1i(uniform_lut, TextureUnits::LightingLUT.id); - } - - GLint uniform_fog_lut = glGetUniformLocation(shader->shader.handle, "fog_lut"); - if (uniform_fog_lut != -1) { - glUniform1i(uniform_fog_lut, TextureUnits::FogLUT.id); - } - - GLint uniform_proctex_noise_lut = - glGetUniformLocation(shader->shader.handle, "proctex_noise_lut"); - if (uniform_proctex_noise_lut != -1) { - glUniform1i(uniform_proctex_noise_lut, TextureUnits::ProcTexNoiseLUT.id); - } - - GLint uniform_proctex_color_map = - glGetUniformLocation(shader->shader.handle, "proctex_color_map"); - if (uniform_proctex_color_map != -1) { - glUniform1i(uniform_proctex_color_map, TextureUnits::ProcTexColorMap.id); - } - - GLint uniform_proctex_alpha_map = - glGetUniformLocation(shader->shader.handle, "proctex_alpha_map"); - if (uniform_proctex_alpha_map != -1) { - glUniform1i(uniform_proctex_alpha_map, TextureUnits::ProcTexAlphaMap.id); - } - - GLint uniform_proctex_lut = glGetUniformLocation(shader->shader.handle, "proctex_lut"); - if (uniform_proctex_lut != -1) { - glUniform1i(uniform_proctex_lut, TextureUnits::ProcTexLUT.id); - } - - GLint uniform_proctex_diff_lut = - glGetUniformLocation(shader->shader.handle, "proctex_diff_lut"); - if (uniform_proctex_diff_lut != -1) { - glUniform1i(uniform_proctex_diff_lut, TextureUnits::ProcTexDiffLUT.id); - } - - current_shader = shader_cache.emplace(config, std::move(shader)).first->second.get(); - - 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); - } - - SyncFogColor(); - SyncProcTexNoise(); - } - } -} - -void RasterizerOpenGL::SyncClipEnabled() { - state.clip_distance[1] = Pica::g_state.regs.rasterizer.clip_enable != 0; -} - -void RasterizerOpenGL::SyncClipCoef() { - const auto raw_clip_coef = Pica::g_state.regs.rasterizer.GetClipCoef(); - const GLvec4 new_clip_coef = {raw_clip_coef.x.ToFloat32(), raw_clip_coef.y.ToFloat32(), - raw_clip_coef.z.ToFloat32(), raw_clip_coef.w.ToFloat32()}; - if (new_clip_coef != uniform_block_data.data.clip_coef) { - uniform_block_data.data.clip_coef = new_clip_coef; - uniform_block_data.dirty = true; - } -} - -void RasterizerOpenGL::SyncCullMode() { - const auto& regs = Pica::g_state.regs; - - switch (regs.rasterizer.cull_mode) { - case Pica::RasterizerRegs::CullMode::KeepAll: - state.cull.enabled = false; - break; - - case Pica::RasterizerRegs::CullMode::KeepClockWise: - state.cull.enabled = true; - state.cull.front_face = GL_CW; - break; - - 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.rasterizer.cull_mode.Value()); - UNIMPLEMENTED(); - break; - } -} - -void RasterizerOpenGL::SyncDepthScale() { - 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; - } -} - -void RasterizerOpenGL::SyncDepthOffset() { - float depth_offset = - 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; - } -} - -void RasterizerOpenGL::SyncBlendEnabled() { - 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.framebuffer.output_merger.alpha_blending.blend_equation_rgb); - state.blend.a_equation = - PicaToGL::BlendEquation(regs.framebuffer.output_merger.alpha_blending.blend_equation_a); - state.blend.src_rgb_func = - PicaToGL::BlendFunc(regs.framebuffer.output_merger.alpha_blending.factor_source_rgb); - state.blend.dst_rgb_func = - 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.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]; - state.blend.color.alpha = blend_color[3]; -} - -void RasterizerOpenGL::SyncFogColor() { - const auto& regs = Pica::g_state.regs; - uniform_block_data.data.fog_color = { - 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; -} - -void RasterizerOpenGL::SyncFogLUT() { - std::array<GLvec2, 128> new_data; - - std::transform(Pica::g_state.fog.lut.begin(), Pica::g_state.fog.lut.end(), new_data.begin(), - [](const auto& entry) { - return GLvec2{entry.ToFloat(), entry.DiffToFloat()}; - }); - - if (new_data != fog_lut_data) { - fog_lut_data = new_data; - glBindBuffer(GL_TEXTURE_BUFFER, fog_lut_buffer.handle); - glBufferSubData(GL_TEXTURE_BUFFER, 0, new_data.size() * sizeof(GLvec2), new_data.data()); - } -} - -void RasterizerOpenGL::SyncProcTexNoise() { - const auto& regs = Pica::g_state.regs.texturing; - uniform_block_data.data.proctex_noise_f = { - Pica::float16::FromRaw(regs.proctex_noise_frequency.u).ToFloat32(), - Pica::float16::FromRaw(regs.proctex_noise_frequency.v).ToFloat32(), - }; - uniform_block_data.data.proctex_noise_a = { - regs.proctex_noise_u.amplitude / 4095.0f, regs.proctex_noise_v.amplitude / 4095.0f, - }; - uniform_block_data.data.proctex_noise_p = { - Pica::float16::FromRaw(regs.proctex_noise_u.phase).ToFloat32(), - Pica::float16::FromRaw(regs.proctex_noise_v.phase).ToFloat32(), - }; - - uniform_block_data.dirty = true; -} - -// helper function for SyncProcTexNoiseLUT/ColorMap/AlphaMap -static void SyncProcTexValueLUT(const std::array<Pica::State::ProcTex::ValueEntry, 128>& lut, - std::array<GLvec2, 128>& lut_data, GLuint buffer) { - std::array<GLvec2, 128> new_data; - std::transform(lut.begin(), lut.end(), new_data.begin(), [](const auto& entry) { - return GLvec2{entry.ToFloat(), entry.DiffToFloat()}; - }); - - if (new_data != lut_data) { - lut_data = new_data; - glBindBuffer(GL_TEXTURE_BUFFER, buffer); - glBufferSubData(GL_TEXTURE_BUFFER, 0, new_data.size() * sizeof(GLvec2), new_data.data()); - } -} - -void RasterizerOpenGL::SyncProcTexNoiseLUT() { - SyncProcTexValueLUT(Pica::g_state.proctex.noise_table, proctex_noise_lut_data, - proctex_noise_lut_buffer.handle); -} - -void RasterizerOpenGL::SyncProcTexColorMap() { - SyncProcTexValueLUT(Pica::g_state.proctex.color_map_table, proctex_color_map_data, - proctex_color_map_buffer.handle); -} - -void RasterizerOpenGL::SyncProcTexAlphaMap() { - SyncProcTexValueLUT(Pica::g_state.proctex.alpha_map_table, proctex_alpha_map_data, - proctex_alpha_map_buffer.handle); -} - -void RasterizerOpenGL::SyncProcTexLUT() { - std::array<GLvec4, 256> new_data; - - std::transform(Pica::g_state.proctex.color_table.begin(), - Pica::g_state.proctex.color_table.end(), new_data.begin(), - [](const auto& entry) { - auto rgba = entry.ToVector() / 255.0f; - return GLvec4{rgba.r(), rgba.g(), rgba.b(), rgba.a()}; - }); - - if (new_data != proctex_lut_data) { - proctex_lut_data = new_data; - glBindBuffer(GL_TEXTURE_BUFFER, proctex_lut_buffer.handle); - glBufferSubData(GL_TEXTURE_BUFFER, 0, new_data.size() * sizeof(GLvec4), new_data.data()); - } -} - -void RasterizerOpenGL::SyncProcTexDiffLUT() { - std::array<GLvec4, 256> new_data; - - std::transform(Pica::g_state.proctex.color_diff_table.begin(), - Pica::g_state.proctex.color_diff_table.end(), new_data.begin(), - [](const auto& entry) { - auto rgba = entry.ToVector() / 255.0f; - return GLvec4{rgba.r(), rgba.g(), rgba.b(), rgba.a()}; - }); - - if (new_data != proctex_diff_lut_data) { - proctex_diff_lut_data = new_data; - glBindBuffer(GL_TEXTURE_BUFFER, proctex_diff_lut_buffer.handle); - glBufferSubData(GL_TEXTURE_BUFFER, 0, new_data.size() * sizeof(GLvec4), new_data.data()); - } -} - -void RasterizerOpenGL::SyncAlphaTest() { - const auto& regs = Pica::g_state.regs; - 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.framebuffer.output_merger.logic_op); -} - -void RasterizerOpenGL::SyncColorWriteMask() { - const auto& regs = Pica::g_state.regs; - - auto IsColorWriteEnabled = [&](u32 value) { - return (regs.framebuffer.framebuffer.allow_color_write != 0 && value != 0) ? GL_TRUE - : GL_FALSE; - }; - - 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.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.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.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.framebuffer.output_merger.stencil_test.action_stencil_fail); - state.stencil.action_depth_fail = - PicaToGL::StencilOp(regs.framebuffer.output_merger.stencil_test.action_depth_fail); - state.stencil.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.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.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; - } -} - -void RasterizerOpenGL::SyncTevConstColor(int stage_index, - 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; - uniform_block_data.dirty = true; - } -} - -void RasterizerOpenGL::SyncGlobalAmbient() { - auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.global_ambient); - if (color != uniform_block_data.data.lighting_global_ambient) { - uniform_block_data.data.lighting_global_ambient = color; - uniform_block_data.dirty = true; - } -} - -void RasterizerOpenGL::SyncLightingLUT(unsigned lut_index) { - std::array<GLvec2, 256> new_data; - const auto& source_lut = Pica::g_state.lighting.luts[lut_index]; - std::transform(source_lut.begin(), source_lut.end(), new_data.begin(), [](const auto& entry) { - return GLvec2{entry.ToFloat(), entry.DiffToFloat()}; - }); - - if (new_data != lighting_lut_data[lut_index]) { - lighting_lut_data[lut_index] = new_data; - glBindBuffer(GL_TEXTURE_BUFFER, lighting_lut_buffer.handle); - glBufferSubData(GL_TEXTURE_BUFFER, lut_index * new_data.size() * sizeof(GLvec2), - new_data.size() * sizeof(GLvec2), new_data.data()); - } -} - -void RasterizerOpenGL::SyncLightSpecular0(int light_index) { - auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.light[light_index].specular_0); - if (color != uniform_block_data.data.light_src[light_index].specular_0) { - uniform_block_data.data.light_src[light_index].specular_0 = color; - uniform_block_data.dirty = true; - } -} - -void RasterizerOpenGL::SyncLightSpecular1(int light_index) { - auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.light[light_index].specular_1); - if (color != uniform_block_data.data.light_src[light_index].specular_1) { - uniform_block_data.data.light_src[light_index].specular_1 = color; - uniform_block_data.dirty = true; - } -} - -void RasterizerOpenGL::SyncLightDiffuse(int light_index) { - auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.light[light_index].diffuse); - if (color != uniform_block_data.data.light_src[light_index].diffuse) { - uniform_block_data.data.light_src[light_index].diffuse = color; - uniform_block_data.dirty = true; - } -} - -void RasterizerOpenGL::SyncLightAmbient(int light_index) { - auto color = PicaToGL::LightColor(Pica::g_state.regs.lighting.light[light_index].ambient); - if (color != uniform_block_data.data.light_src[light_index].ambient) { - uniform_block_data.data.light_src[light_index].ambient = color; - uniform_block_data.dirty = true; - } -} - -void RasterizerOpenGL::SyncLightPosition(int light_index) { - GLvec3 position = { - Pica::float16::FromRaw(Pica::g_state.regs.lighting.light[light_index].x).ToFloat32(), - Pica::float16::FromRaw(Pica::g_state.regs.lighting.light[light_index].y).ToFloat32(), - Pica::float16::FromRaw(Pica::g_state.regs.lighting.light[light_index].z).ToFloat32()}; - - if (position != uniform_block_data.data.light_src[light_index].position) { - uniform_block_data.data.light_src[light_index].position = position; - uniform_block_data.dirty = true; - } -} - -void RasterizerOpenGL::SyncLightSpotDirection(int light_index) { - const auto& light = Pica::g_state.regs.lighting.light[light_index]; - GLvec3 spot_direction = {light.spot_x / 2047.0f, light.spot_y / 2047.0f, - light.spot_z / 2047.0f}; - - if (spot_direction != uniform_block_data.data.light_src[light_index].spot_direction) { - uniform_block_data.data.light_src[light_index].spot_direction = spot_direction; - uniform_block_data.dirty = true; - } -} - -void RasterizerOpenGL::SyncLightDistanceAttenuationBias(int light_index) { - GLfloat dist_atten_bias = - Pica::float20::FromRaw(Pica::g_state.regs.lighting.light[light_index].dist_atten_bias) - .ToFloat32(); - - if (dist_atten_bias != uniform_block_data.data.light_src[light_index].dist_atten_bias) { - uniform_block_data.data.light_src[light_index].dist_atten_bias = dist_atten_bias; - uniform_block_data.dirty = true; - } -} - -void RasterizerOpenGL::SyncLightDistanceAttenuationScale(int light_index) { - GLfloat dist_atten_scale = - Pica::float20::FromRaw(Pica::g_state.regs.lighting.light[light_index].dist_atten_scale) - .ToFloat32(); - - if (dist_atten_scale != uniform_block_data.data.light_src[light_index].dist_atten_scale) { - uniform_block_data.data.light_src[light_index].dist_atten_scale = dist_atten_scale; - uniform_block_data.dirty = true; - } -} diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h deleted file mode 100644 index d02c157e8..000000000 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <array> -#include <cstddef> -#include <cstring> -#include <memory> -#include <unordered_map> -#include <vector> -#include <glad/glad.h> -#include "common/bit_field.h" -#include "common/common_types.h" -#include "common/hash.h" -#include "common/vector_math.h" -#include "core/hw/gpu.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_shader_gen.h" -#include "video_core/renderer_opengl/gl_state.h" -#include "video_core/renderer_opengl/pica_to_gl.h" -#include "video_core/shader/shader.h" - -struct ScreenInfo; - -class RasterizerOpenGL : public VideoCore::RasterizerInterface { -public: - RasterizerOpenGL(); - ~RasterizerOpenGL() override; - - void AddTriangle(const Pica::Shader::OutputVertex& v0, const Pica::Shader::OutputVertex& v1, - const Pica::Shader::OutputVertex& v2) override; - void DrawTriangles() override; - void NotifyPicaRegisterChanged(u32 id) override; - void FlushAll() override; - void FlushRegion(PAddr addr, u64 size) override; - void FlushAndInvalidateRegion(PAddr addr, u64 size) override; - bool AccelerateDisplayTransfer(const GPU::Regs::DisplayTransferConfig& config) override; - bool AccelerateTextureCopy(const GPU::Regs::DisplayTransferConfig& config) override; - bool AccelerateFill(const GPU::Regs::MemoryFillConfig& config) override; - bool AccelerateDisplay(const GPU::Regs::FramebufferConfig& config, PAddr framebuffer_addr, - u32 pixel_stride, ScreenInfo& screen_info) override; - - /// OpenGL shader generated for a given Pica register state - struct PicaShader { - /// OpenGL shader resource - OGLShader shader; - }; - -private: - struct SamplerInfo { - using TextureConfig = Pica::TexturingRegs::TextureConfig; - - OGLSampler sampler; - - /// Creates the sampler object, initializing its state so that it's in sync with the - /// SamplerInfo struct. - void Create(); - /// Syncs the sampler object with the config, updating any necessary state. - void SyncWithConfig(const TextureConfig& config); - - private: - TextureConfig::TextureFilter mag_filter; - TextureConfig::TextureFilter min_filter; - TextureConfig::WrapMode wrap_s; - TextureConfig::WrapMode wrap_t; - u32 border_color; - }; - - /// Structure that the hardware rendered vertices are composed of - struct HardwareVertex { - HardwareVertex(const Pica::Shader::OutputVertex& v, bool flip_quaternion) { - position[0] = v.pos.x.ToFloat32(); - position[1] = v.pos.y.ToFloat32(); - position[2] = v.pos.z.ToFloat32(); - position[3] = v.pos.w.ToFloat32(); - color[0] = v.color.x.ToFloat32(); - color[1] = v.color.y.ToFloat32(); - color[2] = v.color.z.ToFloat32(); - color[3] = v.color.w.ToFloat32(); - tex_coord0[0] = v.tc0.x.ToFloat32(); - tex_coord0[1] = v.tc0.y.ToFloat32(); - tex_coord1[0] = v.tc1.x.ToFloat32(); - tex_coord1[1] = v.tc1.y.ToFloat32(); - tex_coord2[0] = v.tc2.x.ToFloat32(); - tex_coord2[1] = v.tc2.y.ToFloat32(); - tex_coord0_w = v.tc0_w.ToFloat32(); - normquat[0] = v.quat.x.ToFloat32(); - normquat[1] = v.quat.y.ToFloat32(); - normquat[2] = v.quat.z.ToFloat32(); - normquat[3] = v.quat.w.ToFloat32(); - view[0] = v.view.x.ToFloat32(); - view[1] = v.view.y.ToFloat32(); - view[2] = v.view.z.ToFloat32(); - - if (flip_quaternion) { - for (float& x : normquat) { - x = -x; - } - } - } - - GLfloat position[4]; - GLfloat color[4]; - GLfloat tex_coord0[2]; - GLfloat tex_coord1[2]; - GLfloat tex_coord2[2]; - GLfloat tex_coord0_w; - GLfloat normquat[4]; - GLfloat view[3]; - }; - - struct LightSrc { - alignas(16) GLvec3 specular_0; - alignas(16) GLvec3 specular_1; - alignas(16) GLvec3 diffuse; - alignas(16) GLvec3 ambient; - alignas(16) GLvec3 position; - alignas(16) GLvec3 spot_direction; // negated - GLfloat dist_atten_bias; - GLfloat dist_atten_scale; - }; - - /// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned - // NOTE: Always keep a vec4 at the end. The GL spec is not clear wether the alignment at - // the end of a uniform block is included in UNIFORM_BLOCK_DATA_SIZE or not. - // Not following that rule will cause problems on some AMD drivers. - struct UniformData { - alignas(8) GLvec2 framebuffer_scale; - GLint alphatest_ref; - GLfloat depth_scale; - GLfloat depth_offset; - GLint scissor_x1; - GLint scissor_y1; - GLint scissor_x2; - GLint scissor_y2; - alignas(16) GLvec3 fog_color; - alignas(8) GLvec2 proctex_noise_f; - alignas(8) GLvec2 proctex_noise_a; - alignas(8) GLvec2 proctex_noise_p; - alignas(16) GLvec3 lighting_global_ambient; - LightSrc light_src[8]; - alignas(16) GLvec4 const_color[6]; // A vec4 color for each of the six tev stages - alignas(16) GLvec4 tev_combiner_buffer_color; - alignas(16) GLvec4 clip_coef; - }; - - static_assert( - sizeof(UniformData) == 0x470, - "The size of the UniformData structure has changed, update the structure in the shader"); - static_assert(sizeof(UniformData) < 16384, - "UniformData structure must be less than 16kb as per the OpenGL spec"); - - /// Syncs the clip enabled status to match the PICA register - void SyncClipEnabled(); - - /// Syncs the clip coefficients to match the PICA register - void SyncClipCoef(); - - /// Sets the OpenGL shader in accordance with the current PICA register state - void SetShader(); - - /// Syncs the cull mode to match the PICA register - void SyncCullMode(); - - /// Syncs the depth scale to match the PICA register - void SyncDepthScale(); - - /// Syncs the depth offset to match the PICA register - void SyncDepthOffset(); - - /// Syncs the blend enabled status to match the PICA register - void SyncBlendEnabled(); - - /// Syncs the blend functions to match the PICA register - void SyncBlendFuncs(); - - /// Syncs the blend color to match the PICA register - void SyncBlendColor(); - - /// Syncs the fog states to match the PICA register - void SyncFogColor(); - void SyncFogLUT(); - - /// Sync the procedural texture noise configuration to match the PICA register - void SyncProcTexNoise(); - - /// Sync the procedural texture lookup tables - void SyncProcTexNoiseLUT(); - void SyncProcTexColorMap(); - void SyncProcTexAlphaMap(); - void SyncProcTexLUT(); - void SyncProcTexDiffLUT(); - - /// Syncs the alpha test states to match the PICA register - void SyncAlphaTest(); - - /// Syncs the logic op states to match the PICA register - void SyncLogicOp(); - - /// Syncs the color write mask to match the PICA register state - void SyncColorWriteMask(); - - /// Syncs the stencil write mask to match the PICA register state - void SyncStencilWriteMask(); - - /// Syncs the depth write mask to match the PICA register state - void SyncDepthWriteMask(); - - /// Syncs the stencil test states to match the PICA register - void SyncStencilTest(); - - /// Syncs the depth test states to match the PICA register - void SyncDepthTest(); - - /// Syncs the TEV combiner color buffer to match the PICA register - void SyncCombinerColor(); - - /// Syncs the TEV constant color to match the PICA register - void SyncTevConstColor(int tev_index, const Pica::TexturingRegs::TevStageConfig& tev_stage); - - /// Syncs the lighting global ambient color to match the PICA register - void SyncGlobalAmbient(); - - /// Syncs the lighting lookup tables - void SyncLightingLUT(unsigned index); - - /// Syncs the specified light's specular 0 color to match the PICA register - void SyncLightSpecular0(int light_index); - - /// Syncs the specified light's specular 1 color to match the PICA register - void SyncLightSpecular1(int light_index); - - /// Syncs the specified light's diffuse color to match the PICA register - void SyncLightDiffuse(int light_index); - - /// Syncs the specified light's ambient color to match the PICA register - void SyncLightAmbient(int light_index); - - /// Syncs the specified light's position to match the PICA register - void SyncLightPosition(int light_index); - - /// Syncs the specified spot light direcition to match the PICA register - void SyncLightSpotDirection(int light_index); - - /// Syncs the specified light's distance attenuation bias to match the PICA register - void SyncLightDistanceAttenuationBias(int light_index); - - /// Syncs the specified light's distance attenuation scale to match the PICA register - void SyncLightDistanceAttenuationScale(int light_index); - - OpenGLState state; - - RasterizerCacheOpenGL res_cache; - - std::vector<HardwareVertex> vertex_batch; - - std::unordered_map<GLShader::PicaShaderConfig, std::unique_ptr<PicaShader>> shader_cache; - const PicaShader* current_shader = nullptr; - bool shader_dirty; - - struct { - UniformData data; - std::array<bool, Pica::LightingRegs::NumLightingSampler> lut_dirty; - bool fog_lut_dirty; - bool proctex_noise_lut_dirty; - bool proctex_color_map_dirty; - bool proctex_alpha_map_dirty; - bool proctex_lut_dirty; - bool proctex_diff_lut_dirty; - bool dirty; - } uniform_block_data = {}; - - std::array<SamplerInfo, 3> texture_samplers; - OGLVertexArray vertex_array; - OGLBuffer vertex_buffer; - OGLBuffer uniform_buffer; - OGLFramebuffer framebuffer; - - OGLBuffer lighting_lut_buffer; - OGLTexture lighting_lut; - std::array<std::array<GLvec2, 256>, Pica::LightingRegs::NumLightingSampler> lighting_lut_data{}; - - OGLBuffer fog_lut_buffer; - OGLTexture fog_lut; - std::array<GLvec2, 128> fog_lut_data{}; - - OGLBuffer proctex_noise_lut_buffer; - OGLTexture proctex_noise_lut; - std::array<GLvec2, 128> proctex_noise_lut_data{}; - - OGLBuffer proctex_color_map_buffer; - OGLTexture proctex_color_map; - std::array<GLvec2, 128> proctex_color_map_data{}; - - OGLBuffer proctex_alpha_map_buffer; - OGLTexture proctex_alpha_map; - std::array<GLvec2, 128> proctex_alpha_map_data{}; - - OGLBuffer proctex_lut_buffer; - OGLTexture proctex_lut; - std::array<GLvec4, 256> proctex_lut_data{}; - - OGLBuffer proctex_diff_lut_buffer; - OGLTexture proctex_diff_lut; - std::array<GLvec4, 256> proctex_diff_lut_data{}; -}; diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp deleted file mode 100644 index f37894e7a..000000000 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ /dev/null @@ -1,799 +0,0 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <algorithm> -#include <atomic> -#include <cstring> -#include <iterator> -#include <unordered_set> -#include <utility> -#include <vector> -#include <glad/glad.h> -#include "common/bit_field.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 "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" - -struct FormatTuple { - GLint internal_format; - GLenum format; - GLenum type; -}; - -static const std::array<FormatTuple, 5> fb_format_tuples = {{ - {GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8}, // RGBA8 - {GL_RGB8, GL_BGR, GL_UNSIGNED_BYTE}, // RGB8 - {GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // RGB5A1 - {GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // RGB565 - {GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4}, // RGBA4 -}}; - -static const std::array<FormatTuple, 4> depth_format_tuples = {{ - {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16 - {}, - {GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT}, // D24 - {GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // D24S8 -}}; - -RasterizerCacheOpenGL::RasterizerCacheOpenGL() { - transfer_framebuffers[0].Create(); - transfer_framebuffers[1].Create(); -} - -RasterizerCacheOpenGL::~RasterizerCacheOpenGL() { - FlushAll(); -} - -static void MortonCopyPixels(CachedSurface::PixelFormat pixel_format, u32 width, u32 height, - u32 bytes_per_pixel, u32 gl_bytes_per_pixel, u8* morton_data, - u8* gl_data, bool morton_to_gl) { - using PixelFormat = CachedSurface::PixelFormat; - - u8* data_ptrs[2]; - u32 depth_stencil_shifts[2] = {24, 8}; - - if (morton_to_gl) { - std::swap(depth_stencil_shifts[0], depth_stencil_shifts[1]); - } - - if (pixel_format == PixelFormat::D24S8) { - for (unsigned y = 0; y < height; ++y) { - for (unsigned x = 0; x < width; ++x) { - const u32 coarse_y = y & ~7; - u32 morton_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + - coarse_y * width * bytes_per_pixel; - u32 gl_pixel_index = (x + (height - 1 - y) * width) * gl_bytes_per_pixel; - - data_ptrs[morton_to_gl] = morton_data + morton_offset; - data_ptrs[!morton_to_gl] = &gl_data[gl_pixel_index]; - - // Swap depth and stencil value ordering since 3DS does not match OpenGL - u32 depth_stencil; - memcpy(&depth_stencil, data_ptrs[1], sizeof(u32)); - depth_stencil = (depth_stencil << depth_stencil_shifts[0]) | - (depth_stencil >> depth_stencil_shifts[1]); - - memcpy(data_ptrs[0], &depth_stencil, sizeof(u32)); - } - } - } else { - for (unsigned y = 0; y < height; ++y) { - for (unsigned x = 0; x < width; ++x) { - const u32 coarse_y = y & ~7; - u32 morton_offset = VideoCore::GetMortonOffset(x, y, bytes_per_pixel) + - coarse_y * width * bytes_per_pixel; - u32 gl_pixel_index = (x + (height - 1 - y) * width) * gl_bytes_per_pixel; - - data_ptrs[morton_to_gl] = morton_data + morton_offset; - data_ptrs[!morton_to_gl] = &gl_data[gl_pixel_index]; - - memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel); - } - } - } -} - -void RasterizerCacheOpenGL::BlitTextures(GLuint src_tex, GLuint dst_tex, - CachedSurface::SurfaceType type, - const MathUtil::Rectangle<int>& src_rect, - const MathUtil::Rectangle<int>& dst_rect) { - using SurfaceType = CachedSurface::SurfaceType; - - OpenGLState cur_state = OpenGLState::GetCurState(); - - // Make sure textures aren't bound to texture units, since going to bind them to framebuffer - // components - OpenGLState::ResetTexture(src_tex); - OpenGLState::ResetTexture(dst_tex); - - // Keep track of previous framebuffer bindings - GLuint old_fbs[2] = {cur_state.draw.read_framebuffer, cur_state.draw.draw_framebuffer}; - cur_state.draw.read_framebuffer = transfer_framebuffers[0].handle; - cur_state.draw.draw_framebuffer = transfer_framebuffers[1].handle; - cur_state.Apply(); - - u32 buffers = 0; - - if (type == SurfaceType::Color || type == SurfaceType::Texture) { - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, src_tex, - 0); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, - 0); - - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex, - 0); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, - 0); - - buffers = GL_COLOR_BUFFER_BIT; - } else if (type == SurfaceType::Depth) { - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, src_tex, 0); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); - - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, dst_tex, 0); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); - - buffers = GL_DEPTH_BUFFER_BIT; - } else if (type == SurfaceType::DepthStencil) { - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, - src_tex, 0); - - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); - glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, - dst_tex, 0); - - buffers = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT; - } - - 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(); -} - -bool RasterizerCacheOpenGL::TryBlitSurfaces(CachedSurface* src_surface, - const MathUtil::Rectangle<int>& src_rect, - CachedSurface* dst_surface, - const MathUtil::Rectangle<int>& dst_rect) { - - if (!CachedSurface::CheckFormatsBlittable(src_surface->pixel_format, - dst_surface->pixel_format)) { - return false; - } - - 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, - u32 width, u32 height) { - // Allocate an uninitialized texture of appropriate size and format for the surface - using SurfaceType = CachedSurface::SurfaceType; - - OpenGLState cur_state = OpenGLState::GetCurState(); - - // Keep track of previous texture bindings - GLuint old_tex = cur_state.texture_units[0].texture_2d; - cur_state.texture_units[0].texture_2d = texture; - cur_state.Apply(); - glActiveTexture(GL_TEXTURE0); - - SurfaceType type = CachedSurface::GetFormatType(pixel_format); - - FormatTuple tuple; - if (type == SurfaceType::Color) { - ASSERT((size_t)pixel_format < fb_format_tuples.size()); - tuple = fb_format_tuples[(unsigned int)pixel_format]; - } else if (type == SurfaceType::Depth || type == SurfaceType::DepthStencil) { - size_t tuple_idx = (size_t)pixel_format - 14; - ASSERT(tuple_idx < depth_format_tuples.size()); - tuple = depth_format_tuples[tuple_idx]; - } else { - tuple = {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}; - } - - glTexImage2D(GL_TEXTURE_2D, 0, tuple.internal_format, width, height, 0, tuple.format, - tuple.type, nullptr); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - // Restore previous texture bindings - cur_state.texture_units[0].texture_2d = old_tex; - cur_state.Apply(); -} - -MICROPROFILE_DEFINE(OpenGL_SurfaceUpload, "OpenGL", "Surface Upload", MP_RGB(128, 64, 192)); -CachedSurface* RasterizerCacheOpenGL::GetSurface(const CachedSurface& params, bool match_res_scale, - bool load_if_create) { - using PixelFormat = CachedSurface::PixelFormat; - using SurfaceType = CachedSurface::SurfaceType; - - if (params.addr == 0) { - return nullptr; - } - - u32 params_size = - params.width * params.height * CachedSurface::GetFormatBpp(params.pixel_format) / 8; - - // Check for an exact match in existing surfaces - CachedSurface* best_exact_surface = nullptr; - float exact_surface_goodness = -1.f; - - auto surface_interval = - boost::icl::interval<PAddr>::right_open(params.addr, params.addr + params_size); - auto range = surface_cache.equal_range(surface_interval); - for (auto it = range.first; it != range.second; ++it) { - for (auto it2 = it->second.begin(); it2 != it->second.end(); ++it2) { - CachedSurface* surface = it2->get(); - - // Check if the request matches the surface exactly - if (params.addr == surface->addr && params.width == surface->width && - params.height == surface->height && params.pixel_format == surface->pixel_format) { - // Make sure optional param-matching criteria are fulfilled - bool tiling_match = (params.is_tiled == surface->is_tiled); - bool res_scale_match = (params.res_scale_width == surface->res_scale_width && - params.res_scale_height == surface->res_scale_height); - if (!match_res_scale || res_scale_match) { - // Prioritize same-tiling and highest resolution surfaces - float match_goodness = - (float)tiling_match + surface->res_scale_width * surface->res_scale_height; - if (match_goodness > exact_surface_goodness || surface->dirty) { - exact_surface_goodness = match_goodness; - best_exact_surface = surface; - } - } - } - } - } - - // Return the best exact surface if found - if (best_exact_surface != nullptr) { - return best_exact_surface; - } - - // No matching surfaces found, so create a new one - u8* texture_src_data = Memory::GetPhysicalPointer(params.addr); - if (texture_src_data == nullptr) { - return nullptr; - } - - MICROPROFILE_SCOPE(OpenGL_SurfaceUpload); - - // Stride only applies to linear images. - ASSERT(params.pixel_stride == 0 || !params.is_tiled); - - std::shared_ptr<CachedSurface> new_surface = std::make_shared<CachedSurface>(); - - new_surface->addr = params.addr; - new_surface->size = params_size; - - new_surface->texture.Create(); - new_surface->width = params.width; - new_surface->height = params.height; - new_surface->pixel_stride = params.pixel_stride; - new_surface->res_scale_width = params.res_scale_width; - new_surface->res_scale_height = params.res_scale_height; - - new_surface->is_tiled = params.is_tiled; - new_surface->pixel_format = params.pixel_format; - new_surface->dirty = false; - - if (!load_if_create) { - // Don't load any data; just allocate the surface's texture - AllocateSurfaceTexture(new_surface->texture.handle, new_surface->pixel_format, - new_surface->GetScaledWidth(), new_surface->GetScaledHeight()); - } else { - // TODO: Consider attempting subrect match in existing surfaces and direct blit here instead - // of memory upload below if that's a common scenario in some game - - Memory::RasterizerFlushRegion(params.addr, params_size); - - // Load data from memory to the new surface - OpenGLState cur_state = OpenGLState::GetCurState(); - - GLuint old_tex = cur_state.texture_units[0].texture_2d; - cur_state.texture_units[0].texture_2d = new_surface->texture.handle; - cur_state.Apply(); - glActiveTexture(GL_TEXTURE0); - - if (!new_surface->is_tiled) { - // TODO: Ensure this will always be a color format, not a depth or other format - ASSERT((size_t)new_surface->pixel_format < fb_format_tuples.size()); - const FormatTuple& tuple = fb_format_tuples[(unsigned int)params.pixel_format]; - - glPixelStorei(GL_UNPACK_ROW_LENGTH, (GLint)new_surface->pixel_stride); - glTexImage2D(GL_TEXTURE_2D, 0, tuple.internal_format, params.width, params.height, 0, - tuple.format, tuple.type, texture_src_data); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - } else { - SurfaceType type = CachedSurface::GetFormatType(new_surface->pixel_format); - if (type != SurfaceType::Depth && type != SurfaceType::DepthStencil) { - FormatTuple tuple; - if ((size_t)params.pixel_format < fb_format_tuples.size()) { - tuple = fb_format_tuples[(unsigned int)params.pixel_format]; - } else { - // Texture - tuple = {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}; - } - - std::vector<Math::Vec4<u8>> tex_buffer(params.width * params.height); - - Pica::Texture::TextureInfo tex_info; - tex_info.width = params.width; - tex_info.height = params.height; - 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::Texture::LookupTexture( - texture_src_data, x, params.height - 1 - y, tex_info); - } - } - - glTexImage2D(GL_TEXTURE_2D, 0, tuple.internal_format, params.width, params.height, - 0, GL_RGBA, GL_UNSIGNED_BYTE, tex_buffer.data()); - } else { - // Depth/Stencil formats need special treatment since they aren't sampleable using - // LookupTexture and can't use RGBA format - size_t tuple_idx = (size_t)params.pixel_format - 14; - ASSERT(tuple_idx < depth_format_tuples.size()); - const FormatTuple& tuple = depth_format_tuples[tuple_idx]; - - u32 bytes_per_pixel = CachedSurface::GetFormatBpp(params.pixel_format) / 8; - - // OpenGL needs 4 bpp alignment for D24 since using GL_UNSIGNED_INT as type - bool use_4bpp = (params.pixel_format == PixelFormat::D24); - - u32 gl_bytes_per_pixel = use_4bpp ? 4 : bytes_per_pixel; - - std::vector<u8> temp_fb_depth_buffer(params.width * params.height * - gl_bytes_per_pixel); - - u8* temp_fb_depth_buffer_ptr = - use_4bpp ? temp_fb_depth_buffer.data() + 1 : temp_fb_depth_buffer.data(); - - MortonCopyPixels(params.pixel_format, params.width, params.height, bytes_per_pixel, - gl_bytes_per_pixel, texture_src_data, temp_fb_depth_buffer_ptr, - true); - - glTexImage2D(GL_TEXTURE_2D, 0, tuple.internal_format, params.width, params.height, - 0, tuple.format, tuple.type, temp_fb_depth_buffer.data()); - } - } - - // If not 1x scale, blit 1x texture to a new scaled texture and replace texture in surface - if (new_surface->res_scale_width != 1.f || new_surface->res_scale_height != 1.f) { - OGLTexture scaled_texture; - scaled_texture.Create(); - - AllocateSurfaceTexture(scaled_texture.handle, new_surface->pixel_format, - new_surface->GetScaledWidth(), new_surface->GetScaledHeight()); - BlitTextures(new_surface->texture.handle, scaled_texture.handle, - CachedSurface::GetFormatType(new_surface->pixel_format), - MathUtil::Rectangle<int>(0, 0, new_surface->width, new_surface->height), - MathUtil::Rectangle<int>(0, 0, new_surface->GetScaledWidth(), - new_surface->GetScaledHeight())); - - new_surface->texture.Release(); - new_surface->texture.handle = scaled_texture.handle; - scaled_texture.handle = 0; - cur_state.texture_units[0].texture_2d = new_surface->texture.handle; - cur_state.Apply(); - } - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - cur_state.texture_units[0].texture_2d = old_tex; - cur_state.Apply(); - } - - Memory::RasterizerMarkRegionCached(new_surface->addr, new_surface->size, 1); - surface_cache.add(std::make_pair(boost::icl::interval<PAddr>::right_open( - new_surface->addr, new_surface->addr + new_surface->size), - std::set<std::shared_ptr<CachedSurface>>({new_surface}))); - return new_surface.get(); -} - -CachedSurface* RasterizerCacheOpenGL::GetSurfaceRect(const CachedSurface& params, - bool match_res_scale, bool load_if_create, - MathUtil::Rectangle<int>& out_rect) { - if (params.addr == 0) { - return nullptr; - } - - u32 total_pixels = params.width * params.height; - u32 params_size = total_pixels * CachedSurface::GetFormatBpp(params.pixel_format) / 8; - - // Attempt to find encompassing surfaces - CachedSurface* best_subrect_surface = nullptr; - float subrect_surface_goodness = -1.f; - - auto surface_interval = - boost::icl::interval<PAddr>::right_open(params.addr, params.addr + params_size); - auto cache_upper_bound = surface_cache.upper_bound(surface_interval); - for (auto it = surface_cache.lower_bound(surface_interval); it != cache_upper_bound; ++it) { - for (auto it2 = it->second.begin(); it2 != it->second.end(); ++it2) { - CachedSurface* surface = it2->get(); - - // Check if the request is contained in the surface - if (params.addr >= surface->addr && - params.addr + params_size - 1 <= surface->addr + surface->size - 1 && - params.pixel_format == surface->pixel_format) { - // Make sure optional param-matching criteria are fulfilled - bool tiling_match = (params.is_tiled == surface->is_tiled); - bool res_scale_match = (params.res_scale_width == surface->res_scale_width && - params.res_scale_height == surface->res_scale_height); - if (!match_res_scale || res_scale_match) { - // Prioritize same-tiling and highest resolution surfaces - float match_goodness = - (float)tiling_match + surface->res_scale_width * surface->res_scale_height; - if (match_goodness > subrect_surface_goodness || surface->dirty) { - subrect_surface_goodness = match_goodness; - best_subrect_surface = surface; - } - } - } - } - } - - // Return the best subrect surface if found - if (best_subrect_surface != nullptr) { - unsigned int bytes_per_pixel = - (CachedSurface::GetFormatBpp(best_subrect_surface->pixel_format) / 8); - - int x0, y0; - - if (!params.is_tiled) { - u32 begin_pixel_index = (params.addr - best_subrect_surface->addr) / bytes_per_pixel; - x0 = begin_pixel_index % best_subrect_surface->width; - y0 = begin_pixel_index / best_subrect_surface->width; - - out_rect = MathUtil::Rectangle<int>(x0, y0, x0 + params.width, y0 + params.height); - } else { - u32 bytes_per_tile = 8 * 8 * bytes_per_pixel; - u32 tiles_per_row = best_subrect_surface->width / 8; - - u32 begin_tile_index = (params.addr - best_subrect_surface->addr) / bytes_per_tile; - x0 = begin_tile_index % tiles_per_row * 8; - y0 = begin_tile_index / tiles_per_row * 8; - - // Tiled surfaces are flipped vertically in the rasterizer vs. 3DS memory. - out_rect = - MathUtil::Rectangle<int>(x0, best_subrect_surface->height - y0, x0 + params.width, - best_subrect_surface->height - (y0 + params.height)); - } - - out_rect.left = (int)(out_rect.left * best_subrect_surface->res_scale_width); - out_rect.right = (int)(out_rect.right * best_subrect_surface->res_scale_width); - out_rect.top = (int)(out_rect.top * best_subrect_surface->res_scale_height); - out_rect.bottom = (int)(out_rect.bottom * best_subrect_surface->res_scale_height); - - return best_subrect_surface; - } - - // No subrect found - create and return a new surface - if (!params.is_tiled) { - out_rect = MathUtil::Rectangle<int>(0, 0, (int)(params.width * params.res_scale_width), - (int)(params.height * params.res_scale_height)); - } else { - out_rect = MathUtil::Rectangle<int>(0, (int)(params.height * params.res_scale_height), - (int)(params.width * params.res_scale_width), 0); - } - - return GetSurface(params, match_res_scale, load_if_create); -} - -CachedSurface* RasterizerCacheOpenGL::GetTextureSurface( - const Pica::TexturingRegs::FullTextureConfig& config) { - - Pica::Texture::TextureInfo info = - Pica::Texture::TextureInfo::FromPicaRegister(config.config, config.format); - - CachedSurface params; - params.addr = info.physical_address; - params.width = info.width; - params.height = info.height; - params.is_tiled = true; - params.pixel_format = CachedSurface::PixelFormatFromTextureFormat(info.format); - return GetSurface(params, false, true); -} - -std::tuple<CachedSurface*, CachedSurface*, MathUtil::Rectangle<int>> -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 - u32 fb_area = config.GetWidth() * config.GetHeight(); - bool framebuffers_overlap = - config.GetColorBufferPhysicalAddress() != 0 && - config.GetDepthBufferPhysicalAddress() != 0 && - MathUtil::IntervalsIntersect( - config.GetColorBufferPhysicalAddress(), - fb_area * GPU::Regs::BytesPerPixel(GPU::Regs::PixelFormat(config.color_format.Value())), - config.GetDepthBufferPhysicalAddress(), - fb_area * Pica::FramebufferRegs::BytesPerDepthPixel(config.depth_format)); - bool using_color_fb = config.GetColorBufferPhysicalAddress() != 0; - bool depth_write_enable = regs.framebuffer.output_merger.depth_write_enable && - regs.framebuffer.framebuffer.allow_depth_stencil_write; - bool using_depth_fb = config.GetDepthBufferPhysicalAddress() != 0 && - (regs.framebuffer.output_merger.depth_test_enable || 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; " - "overlapping framebuffers not supported!"); - using_depth_fb = false; - } - - // get color and depth surfaces - CachedSurface color_params; - CachedSurface depth_params; - color_params.width = depth_params.width = config.GetWidth(); - color_params.height = depth_params.height = config.GetHeight(); - color_params.is_tiled = depth_params.is_tiled = true; - - // Set the internal resolution, assume the same scaling factor for top and bottom screens - float resolution_scale_factor = Settings::values.resolution_factor; - if (resolution_scale_factor == 0.0f) { - // Auto - scale resolution to the window size - resolution_scale_factor = VideoCore::g_emu_window->GetFramebufferLayout().GetScalingRatio(); - } - // Scale the resolution by the specified factor - color_params.res_scale_width = resolution_scale_factor; - depth_params.res_scale_width = resolution_scale_factor; - color_params.res_scale_height = resolution_scale_factor; - depth_params.res_scale_height = resolution_scale_factor; - - color_params.addr = config.GetColorBufferPhysicalAddress(); - color_params.pixel_format = CachedSurface::PixelFormatFromColorFormat(config.color_format); - - depth_params.addr = config.GetDepthBufferPhysicalAddress(); - depth_params.pixel_format = CachedSurface::PixelFormatFromDepthFormat(config.depth_format); - - MathUtil::Rectangle<int> color_rect; - CachedSurface* color_surface = - using_color_fb ? GetSurfaceRect(color_params, true, true, color_rect) : nullptr; - - MathUtil::Rectangle<int> depth_rect; - CachedSurface* depth_surface = - using_depth_fb ? GetSurfaceRect(depth_params, true, true, depth_rect) : nullptr; - - // Sanity check to make sure found surfaces aren't the same - if (using_depth_fb && using_color_fb && color_surface == depth_surface) { - LOG_CRITICAL( - Render_OpenGL, - "Color and depth framebuffer surfaces overlap; overlapping surfaces not supported!"); - using_depth_fb = false; - depth_surface = nullptr; - } - - MathUtil::Rectangle<int> rect; - - if (color_surface != nullptr && depth_surface != nullptr && - (depth_rect.left != color_rect.left || depth_rect.top != color_rect.top)) { - // Can't specify separate color and depth viewport offsets in OpenGL, so re-zero both if - // they don't match - if (color_rect.left != 0 || color_rect.top != 0) { - color_surface = GetSurface(color_params, true, true); - } - - if (depth_rect.left != 0 || depth_rect.top != 0) { - depth_surface = GetSurface(depth_params, true, true); - } - - if (!color_surface->is_tiled) { - rect = MathUtil::Rectangle<int>( - 0, 0, (int)(color_params.width * color_params.res_scale_width), - (int)(color_params.height * color_params.res_scale_height)); - } else { - rect = MathUtil::Rectangle<int>( - 0, (int)(color_params.height * color_params.res_scale_height), - (int)(color_params.width * color_params.res_scale_width), 0); - } - } else if (color_surface != nullptr) { - rect = color_rect; - } else if (depth_surface != nullptr) { - rect = depth_rect; - } else { - rect = MathUtil::Rectangle<int>(0, 0, 0, 0); - } - - return std::make_tuple(color_surface, depth_surface, rect); -} - -CachedSurface* RasterizerCacheOpenGL::TryGetFillSurface(const GPU::Regs::MemoryFillConfig& config) { - auto surface_interval = - boost::icl::interval<PAddr>::right_open(config.GetStartAddress(), config.GetEndAddress()); - auto range = surface_cache.equal_range(surface_interval); - for (auto it = range.first; it != range.second; ++it) { - for (auto it2 = it->second.begin(); it2 != it->second.end(); ++it2) { - int bits_per_value = 0; - if (config.fill_24bit) { - bits_per_value = 24; - } else if (config.fill_32bit) { - bits_per_value = 32; - } else { - bits_per_value = 16; - } - - CachedSurface* surface = it2->get(); - - if (surface->addr == config.GetStartAddress() && - CachedSurface::GetFormatBpp(surface->pixel_format) == bits_per_value && - (surface->width * surface->height * - CachedSurface::GetFormatBpp(surface->pixel_format) / 8) == - (config.GetEndAddress() - config.GetStartAddress())) { - return surface; - } - } - } - - return nullptr; -} - -MICROPROFILE_DEFINE(OpenGL_SurfaceDownload, "OpenGL", "Surface Download", MP_RGB(128, 192, 64)); -void RasterizerCacheOpenGL::FlushSurface(CachedSurface* surface) { - using PixelFormat = CachedSurface::PixelFormat; - using SurfaceType = CachedSurface::SurfaceType; - - if (!surface->dirty) { - return; - } - - MICROPROFILE_SCOPE(OpenGL_SurfaceDownload); - - u8* dst_buffer = Memory::GetPhysicalPointer(surface->addr); - if (dst_buffer == nullptr) { - return; - } - - OpenGLState cur_state = OpenGLState::GetCurState(); - GLuint old_tex = cur_state.texture_units[0].texture_2d; - - OGLTexture unscaled_tex; - GLuint texture_to_flush = surface->texture.handle; - - // If not 1x scale, blit scaled texture to a new 1x texture and use that to flush - if (surface->res_scale_width != 1.f || surface->res_scale_height != 1.f) { - unscaled_tex.Create(); - - AllocateSurfaceTexture(unscaled_tex.handle, surface->pixel_format, surface->width, - surface->height); - BlitTextures( - surface->texture.handle, unscaled_tex.handle, - CachedSurface::GetFormatType(surface->pixel_format), - MathUtil::Rectangle<int>(0, 0, surface->GetScaledWidth(), surface->GetScaledHeight()), - MathUtil::Rectangle<int>(0, 0, surface->width, surface->height)); - - texture_to_flush = unscaled_tex.handle; - } - - cur_state.texture_units[0].texture_2d = texture_to_flush; - cur_state.Apply(); - glActiveTexture(GL_TEXTURE0); - - if (!surface->is_tiled) { - // TODO: Ensure this will always be a color format, not a depth or other format - ASSERT((size_t)surface->pixel_format < fb_format_tuples.size()); - const FormatTuple& tuple = fb_format_tuples[(unsigned int)surface->pixel_format]; - - glPixelStorei(GL_PACK_ROW_LENGTH, (GLint)surface->pixel_stride); - glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, dst_buffer); - glPixelStorei(GL_PACK_ROW_LENGTH, 0); - } else { - SurfaceType type = CachedSurface::GetFormatType(surface->pixel_format); - if (type != SurfaceType::Depth && type != SurfaceType::DepthStencil) { - ASSERT((size_t)surface->pixel_format < fb_format_tuples.size()); - const FormatTuple& tuple = fb_format_tuples[(unsigned int)surface->pixel_format]; - - u32 bytes_per_pixel = CachedSurface::GetFormatBpp(surface->pixel_format) / 8; - - std::vector<u8> temp_gl_buffer(surface->width * surface->height * bytes_per_pixel); - - glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, temp_gl_buffer.data()); - - // Directly copy pixels. Internal OpenGL color formats are consistent so no conversion - // is necessary. - MortonCopyPixels(surface->pixel_format, surface->width, surface->height, - bytes_per_pixel, bytes_per_pixel, dst_buffer, temp_gl_buffer.data(), - false); - } else { - // Depth/Stencil formats need special treatment since they aren't sampleable using - // LookupTexture and can't use RGBA format - size_t tuple_idx = (size_t)surface->pixel_format - 14; - ASSERT(tuple_idx < depth_format_tuples.size()); - const FormatTuple& tuple = depth_format_tuples[tuple_idx]; - - u32 bytes_per_pixel = CachedSurface::GetFormatBpp(surface->pixel_format) / 8; - - // OpenGL needs 4 bpp alignment for D24 since using GL_UNSIGNED_INT as type - bool use_4bpp = (surface->pixel_format == PixelFormat::D24); - - u32 gl_bytes_per_pixel = use_4bpp ? 4 : bytes_per_pixel; - - std::vector<u8> temp_gl_buffer(surface->width * surface->height * gl_bytes_per_pixel); - - glGetTexImage(GL_TEXTURE_2D, 0, tuple.format, tuple.type, temp_gl_buffer.data()); - - u8* temp_gl_buffer_ptr = use_4bpp ? temp_gl_buffer.data() + 1 : temp_gl_buffer.data(); - - MortonCopyPixels(surface->pixel_format, surface->width, surface->height, - bytes_per_pixel, gl_bytes_per_pixel, dst_buffer, temp_gl_buffer_ptr, - false); - } - } - - surface->dirty = false; - - cur_state.texture_units[0].texture_2d = old_tex; - cur_state.Apply(); -} - -void RasterizerCacheOpenGL::FlushRegion(PAddr addr, u32 size, const CachedSurface* skip_surface, - bool invalidate) { - if (size == 0) { - return; - } - - // Gather up unique surfaces that touch the region - std::unordered_set<std::shared_ptr<CachedSurface>> touching_surfaces; - - auto surface_interval = boost::icl::interval<PAddr>::right_open(addr, addr + size); - auto cache_upper_bound = surface_cache.upper_bound(surface_interval); - for (auto it = surface_cache.lower_bound(surface_interval); it != cache_upper_bound; ++it) { - std::copy_if(it->second.begin(), it->second.end(), - std::inserter(touching_surfaces, touching_surfaces.end()), - [skip_surface](std::shared_ptr<CachedSurface> surface) { - return (surface.get() != skip_surface); - }); - } - - // Flush and invalidate surfaces - for (auto surface : touching_surfaces) { - FlushSurface(surface.get()); - if (invalidate) { - Memory::RasterizerMarkRegionCached(surface->addr, surface->size, -1); - surface_cache.subtract( - std::make_pair(boost::icl::interval<PAddr>::right_open( - surface->addr, surface->addr + surface->size), - std::set<std::shared_ptr<CachedSurface>>({surface}))); - } - } -} - -void RasterizerCacheOpenGL::FlushAll() { - for (auto& surfaces : surface_cache) { - for (auto& surface : surfaces.second) { - FlushSurface(surface.get()); - } - } -} diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h deleted file mode 100644 index aea20c693..000000000 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ /dev/null @@ -1,239 +0,0 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <array> -#include <memory> -#include <set> -#include <tuple> -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-local-typedef" -#endif -#include <boost/icl/interval_map.hpp> -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif -#include <glad/glad.h> -#include "common/assert.h" -#include "common/common_funcs.h" -#include "common/common_types.h" -#include "core/hw/gpu.h" -#include "video_core/regs_framebuffer.h" -#include "video_core/regs_texturing.h" -#include "video_core/renderer_opengl/gl_resource_manager.h" - -namespace MathUtil { -template <class T> -struct Rectangle; -} - -struct CachedSurface; - -using SurfaceCache = boost::icl::interval_map<PAddr, std::set<std::shared_ptr<CachedSurface>>>; - -struct CachedSurface { - enum class PixelFormat { - // First 5 formats are shared between textures and color buffers - RGBA8 = 0, - RGB8 = 1, - RGB5A1 = 2, - RGB565 = 3, - RGBA4 = 4, - - // Texture-only formats - IA8 = 5, - RG8 = 6, - I8 = 7, - A8 = 8, - IA4 = 9, - I4 = 10, - A4 = 11, - ETC1 = 12, - ETC1A4 = 13, - - // Depth buffer-only formats - D16 = 14, - // gap - D24 = 16, - D24S8 = 17, - - Invalid = 255, - }; - - enum class SurfaceType { - Color = 0, - Texture = 1, - Depth = 2, - DepthStencil = 3, - Invalid = 4, - }; - - static unsigned int GetFormatBpp(CachedSurface::PixelFormat format) { - static const std::array<unsigned int, 18> bpp_table = { - 32, // RGBA8 - 24, // RGB8 - 16, // RGB5A1 - 16, // RGB565 - 16, // RGBA4 - 16, // IA8 - 16, // RG8 - 8, // I8 - 8, // A8 - 8, // IA4 - 4, // I4 - 4, // A4 - 4, // ETC1 - 8, // ETC1A4 - 16, // D16 - 0, - 24, // D24 - 32, // D24S8 - }; - - ASSERT((unsigned int)format < ARRAY_SIZE(bpp_table)); - return bpp_table[(unsigned int)format]; - } - - static PixelFormat PixelFormatFromTextureFormat(Pica::TexturingRegs::TextureFormat format) { - return ((unsigned int)format < 14) ? (PixelFormat)format : PixelFormat::Invalid; - } - - static PixelFormat PixelFormatFromColorFormat(Pica::FramebufferRegs::ColorFormat format) { - return ((unsigned int)format < 5) ? (PixelFormat)format : PixelFormat::Invalid; - } - - static PixelFormat PixelFormatFromDepthFormat(Pica::FramebufferRegs::DepthFormat format) { - return ((unsigned int)format < 4) ? (PixelFormat)((unsigned int)format + 14) - : PixelFormat::Invalid; - } - - static PixelFormat PixelFormatFromGPUPixelFormat(GPU::Regs::PixelFormat format) { - switch (format) { - // RGB565 and RGB5A1 are switched in PixelFormat compared to ColorFormat - case GPU::Regs::PixelFormat::RGB565: - return PixelFormat::RGB565; - case GPU::Regs::PixelFormat::RGB5A1: - return PixelFormat::RGB5A1; - default: - return ((unsigned int)format < 5) ? (PixelFormat)format : PixelFormat::Invalid; - } - } - - static bool CheckFormatsBlittable(PixelFormat pixel_format_a, PixelFormat pixel_format_b) { - SurfaceType a_type = GetFormatType(pixel_format_a); - SurfaceType b_type = GetFormatType(pixel_format_b); - - if ((a_type == SurfaceType::Color || a_type == SurfaceType::Texture) && - (b_type == SurfaceType::Color || b_type == SurfaceType::Texture)) { - return true; - } - - if (a_type == SurfaceType::Depth && b_type == SurfaceType::Depth) { - return true; - } - - if (a_type == SurfaceType::DepthStencil && b_type == SurfaceType::DepthStencil) { - return true; - } - - return false; - } - - static SurfaceType GetFormatType(PixelFormat pixel_format) { - if ((unsigned int)pixel_format < 5) { - return SurfaceType::Color; - } - - if ((unsigned int)pixel_format < 14) { - return SurfaceType::Texture; - } - - if (pixel_format == PixelFormat::D16 || pixel_format == PixelFormat::D24) { - return SurfaceType::Depth; - } - - if (pixel_format == PixelFormat::D24S8) { - return SurfaceType::DepthStencil; - } - - return SurfaceType::Invalid; - } - - u32 GetScaledWidth() const { - return (u32)(width * res_scale_width); - } - - u32 GetScaledHeight() const { - return (u32)(height * res_scale_height); - } - - PAddr addr; - u32 size; - - PAddr min_valid; - PAddr max_valid; - - OGLTexture texture; - u32 width; - u32 height; - /// Stride between lines, in pixels. Only valid for images in linear format. - u32 pixel_stride = 0; - float res_scale_width = 1.f; - float res_scale_height = 1.f; - - bool is_tiled; - PixelFormat pixel_format; - bool dirty; -}; - -class RasterizerCacheOpenGL : NonCopyable { -public: - RasterizerCacheOpenGL(); - ~RasterizerCacheOpenGL(); - - /// Blits one texture to another - void BlitTextures(GLuint src_tex, GLuint dst_tex, CachedSurface::SurfaceType type, - const MathUtil::Rectangle<int>& src_rect, - const MathUtil::Rectangle<int>& dst_rect); - - /// Attempt to blit one surface's texture to another - bool TryBlitSurfaces(CachedSurface* src_surface, const MathUtil::Rectangle<int>& src_rect, - CachedSurface* dst_surface, const MathUtil::Rectangle<int>& dst_rect); - - /// Loads a texture from 3DS memory to OpenGL and caches it (if not already cached) - CachedSurface* GetSurface(const CachedSurface& params, bool match_res_scale, - bool load_if_create); - - /// Attempt to find a subrect (resolution scaled) of a surface, otherwise loads a texture from - /// 3DS memory to OpenGL and caches it (if not already cached) - CachedSurface* GetSurfaceRect(const CachedSurface& params, bool match_res_scale, - bool load_if_create, MathUtil::Rectangle<int>& out_rect); - - /// Gets a surface based on the texture configuration - 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::FramebufferRegs::FramebufferConfig& config); - - /// Attempt to get a surface that exactly matches the fill region and format - CachedSurface* TryGetFillSurface(const GPU::Regs::MemoryFillConfig& config); - - /// Write the surface back to memory - void FlushSurface(CachedSurface* surface); - - /// Write any cached resources overlapping the region back to memory (if dirty) and optionally - /// invalidate them in the cache - void FlushRegion(PAddr addr, u32 size, const CachedSurface* skip_surface, bool invalidate); - - /// Flush all cached resources tracked by this cache manager - void FlushAll(); - -private: - SurfaceCache surface_cache; - OGLFramebuffer transfer_framebuffers[2]; -}; diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp deleted file mode 100644 index 9fe183944..000000000 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ /dev/null @@ -1,1231 +0,0 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <array> -#include <cstddef> -#include <cstring> -#include "common/assert.h" -#include "common/bit_field.h" -#include "common/logging/log.h" -#include "core/core.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::FramebufferRegs; -using Pica::LightingRegs; -using Pica::RasterizerRegs; -using Pica::TexturingRegs; -using TevStageConfig = TexturingRegs::TevStageConfig; - -namespace GLShader { - -static const std::string UniformBlockDef = R"( -#define NUM_TEV_STAGES 6 -#define NUM_LIGHTS 8 - -struct LightSrc { - vec3 specular_0; - vec3 specular_1; - vec3 diffuse; - vec3 ambient; - vec3 position; - vec3 spot_direction; - float dist_atten_bias; - float dist_atten_scale; -}; - -layout (std140) uniform shader_data { - vec2 framebuffer_scale; - int alphatest_ref; - float depth_scale; - float depth_offset; - int scissor_x1; - int scissor_y1; - int scissor_x2; - int scissor_y2; - vec3 fog_color; - vec2 proctex_noise_f; - vec2 proctex_noise_a; - vec2 proctex_noise_p; - vec3 lighting_global_ambient; - LightSrc light_src[NUM_LIGHTS]; - vec4 const_color[NUM_TEV_STAGES]; - vec4 tev_combiner_buffer_color; - vec4 clip_coef; -}; -)"; - -PicaShaderConfig PicaShaderConfig::BuildFromRegs(const Pica::Regs& regs) { - PicaShaderConfig res; - - auto& state = res.state; - std::memset(&state, 0, sizeof(PicaShaderConfig::State)); - - state.scissor_test_mode = regs.rasterizer.scissor_test.mode; - - state.depthmap_enable = regs.rasterizer.depthmap_enable; - - 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.texturing.texture0.type; - - state.texture2_use_coord1 = regs.texturing.main_config.texture2_use_coord1 != 0; - - // 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.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]; - state.tev_stages[i].sources_raw = tev_stage.sources_raw; - state.tev_stages[i].modifiers_raw = tev_stage.modifiers_raw; - state.tev_stages[i].ops_raw = tev_stage.ops_raw; - state.tev_stages[i].scales_raw = tev_stage.scales_raw; - } - - state.fog_mode = regs.texturing.fog_mode; - state.fog_flip = regs.texturing.fog_flip != 0; - - 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.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); - const auto& light = regs.lighting.light[num]; - state.lighting.light[light_index].num = num; - state.lighting.light[light_index].directional = light.config.directional != 0; - state.lighting.light[light_index].two_sided_diffuse = light.config.two_sided_diffuse != 0; - state.lighting.light[light_index].geometric_factor_0 = light.config.geometric_factor_0 != 0; - state.lighting.light[light_index].geometric_factor_1 = light.config.geometric_factor_1 != 0; - state.lighting.light[light_index].dist_atten_enable = - !regs.lighting.IsDistAttenDisabled(num); - state.lighting.light[light_index].spot_atten_enable = - !regs.lighting.IsSpotAttenDisabled(num); - } - - state.lighting.lut_d0.enable = regs.lighting.config1.disable_lut_d0 == 0; - state.lighting.lut_d0.abs_input = regs.lighting.abs_lut_input.disable_d0 == 0; - state.lighting.lut_d0.type = regs.lighting.lut_input.d0.Value(); - state.lighting.lut_d0.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d0); - - state.lighting.lut_d1.enable = regs.lighting.config1.disable_lut_d1 == 0; - state.lighting.lut_d1.abs_input = regs.lighting.abs_lut_input.disable_d1 == 0; - state.lighting.lut_d1.type = regs.lighting.lut_input.d1.Value(); - state.lighting.lut_d1.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.d1); - - // this is a dummy field due to lack of the corresponding register - state.lighting.lut_sp.enable = true; - state.lighting.lut_sp.abs_input = regs.lighting.abs_lut_input.disable_sp == 0; - state.lighting.lut_sp.type = regs.lighting.lut_input.sp.Value(); - state.lighting.lut_sp.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.sp); - - state.lighting.lut_fr.enable = regs.lighting.config1.disable_lut_fr == 0; - state.lighting.lut_fr.abs_input = regs.lighting.abs_lut_input.disable_fr == 0; - state.lighting.lut_fr.type = regs.lighting.lut_input.fr.Value(); - state.lighting.lut_fr.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.fr); - - state.lighting.lut_rr.enable = regs.lighting.config1.disable_lut_rr == 0; - state.lighting.lut_rr.abs_input = regs.lighting.abs_lut_input.disable_rr == 0; - state.lighting.lut_rr.type = regs.lighting.lut_input.rr.Value(); - state.lighting.lut_rr.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rr); - - state.lighting.lut_rg.enable = regs.lighting.config1.disable_lut_rg == 0; - state.lighting.lut_rg.abs_input = regs.lighting.abs_lut_input.disable_rg == 0; - state.lighting.lut_rg.type = regs.lighting.lut_input.rg.Value(); - state.lighting.lut_rg.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rg); - - state.lighting.lut_rb.enable = regs.lighting.config1.disable_lut_rb == 0; - state.lighting.lut_rb.abs_input = regs.lighting.abs_lut_input.disable_rb == 0; - state.lighting.lut_rb.type = regs.lighting.lut_input.rb.Value(); - state.lighting.lut_rb.scale = regs.lighting.lut_scale.GetScale(regs.lighting.lut_scale.rb); - - state.lighting.config = regs.lighting.config0.config; - state.lighting.fresnel_selector = regs.lighting.config0.fresnel_selector; - state.lighting.bump_mode = regs.lighting.config0.bump_mode; - state.lighting.bump_selector = regs.lighting.config0.bump_selector; - state.lighting.bump_renorm = regs.lighting.config0.disable_bump_renorm == 0; - state.lighting.clamp_highlights = regs.lighting.config0.clamp_highlights != 0; - - state.proctex.enable = regs.texturing.main_config.texture3_enable; - if (state.proctex.enable) { - state.proctex.coord = regs.texturing.main_config.texture3_coordinates; - state.proctex.u_clamp = regs.texturing.proctex.u_clamp; - state.proctex.v_clamp = regs.texturing.proctex.v_clamp; - state.proctex.color_combiner = regs.texturing.proctex.color_combiner; - state.proctex.alpha_combiner = regs.texturing.proctex.alpha_combiner; - state.proctex.separate_alpha = regs.texturing.proctex.separate_alpha; - state.proctex.noise_enable = regs.texturing.proctex.noise_enable; - state.proctex.u_shift = regs.texturing.proctex.u_shift; - state.proctex.v_shift = regs.texturing.proctex.v_shift; - state.proctex.lut_width = regs.texturing.proctex_lut.width; - state.proctex.lut_offset = regs.texturing.proctex_lut_offset; - state.proctex.lut_filter = regs.texturing.proctex_lut.filter; - } - - return res; -} - -/// Detects if a TEV stage is configured to be skipped (to avoid generating unnecessary code) -static bool IsPassThroughTevStage(const TevStageConfig& stage) { - return (stage.color_op == TevStageConfig::Operation::Replace && - stage.alpha_op == TevStageConfig::Operation::Replace && - stage.color_source1 == TevStageConfig::Source::Previous && - stage.alpha_source1 == TevStageConfig::Source::Previous && - stage.color_modifier1 == TevStageConfig::ColorModifier::SourceColor && - stage.alpha_modifier1 == TevStageConfig::AlphaModifier::SourceAlpha && - stage.GetColorMultiplier() == 1 && stage.GetAlphaMultiplier() == 1); -} - -static std::string SampleTexture(const PicaShaderConfig& config, unsigned texture_unit) { - const auto& state = config.state; - switch (texture_unit) { - case 0: - // Only unit 0 respects the texturing type - switch (state.texture0_type) { - case TexturingRegs::TextureConfig::Texture2D: - return "texture(tex[0], texcoord[0])"; - case TexturingRegs::TextureConfig::Projection2D: - return "textureProj(tex[0], vec3(texcoord[0], texcoord0_w))"; - default: - LOG_CRITICAL(HW_GPU, "Unhandled texture type %x", - static_cast<int>(state.texture0_type)); - UNIMPLEMENTED(); - return "texture(tex[0], texcoord[0])"; - } - case 1: - return "texture(tex[1], texcoord[1])"; - case 2: - if (state.texture2_use_coord1) - return "texture(tex[2], texcoord[1])"; - else - return "texture(tex[2], texcoord[2])"; - case 3: - if (state.proctex.enable) { - return "ProcTex()"; - } else { - LOG_ERROR(Render_OpenGL, "Using Texture3 without enabling it"); - return "vec4(0.0)"; - } - default: - UNREACHABLE(); - return ""; - } -} - -/// Writes the specified TEV stage source component(s) -static void AppendSource(std::string& out, const PicaShaderConfig& config, - TevStageConfig::Source source, const std::string& index_name) { - const auto& state = config.state; - using Source = TevStageConfig::Source; - switch (source) { - case Source::PrimaryColor: - out += "primary_color"; - break; - case Source::PrimaryFragmentColor: - out += "primary_fragment_color"; - break; - case Source::SecondaryFragmentColor: - out += "secondary_fragment_color"; - break; - case Source::Texture0: - out += SampleTexture(config, 0); - break; - case Source::Texture1: - out += SampleTexture(config, 1); - break; - case Source::Texture2: - out += SampleTexture(config, 2); - break; - case Source::Texture3: - out += SampleTexture(config, 3); - break; - case Source::PreviousBuffer: - out += "combiner_buffer"; - break; - case Source::Constant: - ((out += "const_color[") += index_name) += ']'; - break; - case Source::Previous: - out += "last_tex_env_out"; - break; - default: - out += "vec4(0.0)"; - LOG_CRITICAL(Render_OpenGL, "Unknown source op %u", source); - break; - } -} - -/// Writes the color components to use for the specified TEV stage color modifier -static void AppendColorModifier(std::string& out, const PicaShaderConfig& config, - TevStageConfig::ColorModifier modifier, - TevStageConfig::Source source, const std::string& index_name) { - using ColorModifier = TevStageConfig::ColorModifier; - switch (modifier) { - case ColorModifier::SourceColor: - AppendSource(out, config, source, index_name); - out += ".rgb"; - break; - case ColorModifier::OneMinusSourceColor: - out += "vec3(1.0) - "; - AppendSource(out, config, source, index_name); - out += ".rgb"; - break; - case ColorModifier::SourceAlpha: - AppendSource(out, config, source, index_name); - out += ".aaa"; - break; - case ColorModifier::OneMinusSourceAlpha: - out += "vec3(1.0) - "; - AppendSource(out, config, source, index_name); - out += ".aaa"; - break; - case ColorModifier::SourceRed: - AppendSource(out, config, source, index_name); - out += ".rrr"; - break; - case ColorModifier::OneMinusSourceRed: - out += "vec3(1.0) - "; - AppendSource(out, config, source, index_name); - out += ".rrr"; - break; - case ColorModifier::SourceGreen: - AppendSource(out, config, source, index_name); - out += ".ggg"; - break; - case ColorModifier::OneMinusSourceGreen: - out += "vec3(1.0) - "; - AppendSource(out, config, source, index_name); - out += ".ggg"; - break; - case ColorModifier::SourceBlue: - AppendSource(out, config, source, index_name); - out += ".bbb"; - break; - case ColorModifier::OneMinusSourceBlue: - out += "vec3(1.0) - "; - AppendSource(out, config, source, index_name); - out += ".bbb"; - break; - default: - out += "vec3(0.0)"; - LOG_CRITICAL(Render_OpenGL, "Unknown color modifier op %u", modifier); - break; - } -} - -/// Writes the alpha component to use for the specified TEV stage alpha modifier -static void AppendAlphaModifier(std::string& out, const PicaShaderConfig& config, - TevStageConfig::AlphaModifier modifier, - TevStageConfig::Source source, const std::string& index_name) { - using AlphaModifier = TevStageConfig::AlphaModifier; - switch (modifier) { - case AlphaModifier::SourceAlpha: - AppendSource(out, config, source, index_name); - out += ".a"; - break; - case AlphaModifier::OneMinusSourceAlpha: - out += "1.0 - "; - AppendSource(out, config, source, index_name); - out += ".a"; - break; - case AlphaModifier::SourceRed: - AppendSource(out, config, source, index_name); - out += ".r"; - break; - case AlphaModifier::OneMinusSourceRed: - out += "1.0 - "; - AppendSource(out, config, source, index_name); - out += ".r"; - break; - case AlphaModifier::SourceGreen: - AppendSource(out, config, source, index_name); - out += ".g"; - break; - case AlphaModifier::OneMinusSourceGreen: - out += "1.0 - "; - AppendSource(out, config, source, index_name); - out += ".g"; - break; - case AlphaModifier::SourceBlue: - AppendSource(out, config, source, index_name); - out += ".b"; - break; - case AlphaModifier::OneMinusSourceBlue: - out += "1.0 - "; - AppendSource(out, config, source, index_name); - out += ".b"; - break; - default: - out += "0.0"; - LOG_CRITICAL(Render_OpenGL, "Unknown alpha modifier op %u", modifier); - break; - } -} - -/// Writes the combiner function for the color components for the specified TEV stage operation -static void AppendColorCombiner(std::string& out, TevStageConfig::Operation operation, - const std::string& variable_name) { - out += "clamp("; - using Operation = TevStageConfig::Operation; - switch (operation) { - case Operation::Replace: - out += variable_name + "[0]"; - break; - case Operation::Modulate: - out += variable_name + "[0] * " + variable_name + "[1]"; - break; - case Operation::Add: - out += variable_name + "[0] + " + variable_name + "[1]"; - break; - case Operation::AddSigned: - out += variable_name + "[0] + " + variable_name + "[1] - vec3(0.5)"; - break; - case Operation::Lerp: - out += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + - "[1] * (vec3(1.0) - " + variable_name + "[2])"; - break; - case Operation::Subtract: - out += variable_name + "[0] - " + variable_name + "[1]"; - break; - case Operation::MultiplyThenAdd: - out += variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2]"; - break; - case Operation::AddThenMultiply: - out += "min(" + variable_name + "[0] + " + variable_name + "[1], vec3(1.0)) * " + - variable_name + "[2]"; - break; - case Operation::Dot3_RGB: - case Operation::Dot3_RGBA: - out += "vec3(dot(" + variable_name + "[0] - vec3(0.5), " + variable_name + - "[1] - vec3(0.5)) * 4.0)"; - break; - default: - out += "vec3(0.0)"; - LOG_CRITICAL(Render_OpenGL, "Unknown color combiner operation: %u", operation); - break; - } - out += ", vec3(0.0), vec3(1.0))"; // Clamp result to 0.0, 1.0 -} - -/// Writes the combiner function for the alpha component for the specified TEV stage operation -static void AppendAlphaCombiner(std::string& out, TevStageConfig::Operation operation, - const std::string& variable_name) { - out += "clamp("; - using Operation = TevStageConfig::Operation; - switch (operation) { - case Operation::Replace: - out += variable_name + "[0]"; - break; - case Operation::Modulate: - out += variable_name + "[0] * " + variable_name + "[1]"; - break; - case Operation::Add: - out += variable_name + "[0] + " + variable_name + "[1]"; - break; - case Operation::AddSigned: - out += variable_name + "[0] + " + variable_name + "[1] - 0.5"; - break; - case Operation::Lerp: - out += variable_name + "[0] * " + variable_name + "[2] + " + variable_name + - "[1] * (1.0 - " + variable_name + "[2])"; - break; - case Operation::Subtract: - out += variable_name + "[0] - " + variable_name + "[1]"; - break; - case Operation::MultiplyThenAdd: - out += variable_name + "[0] * " + variable_name + "[1] + " + variable_name + "[2]"; - break; - case Operation::AddThenMultiply: - out += "min(" + variable_name + "[0] + " + variable_name + "[1], 1.0) * " + variable_name + - "[2]"; - break; - default: - out += "0.0"; - LOG_CRITICAL(Render_OpenGL, "Unknown alpha combiner operation: %u", operation); - break; - } - out += ", 0.0, 1.0)"; -} - -/// Writes the if-statement condition used to evaluate alpha testing -static void AppendAlphaTestCondition(std::string& out, FramebufferRegs::CompareFunc func) { - using CompareFunc = FramebufferRegs::CompareFunc; - switch (func) { - case CompareFunc::Never: - out += "true"; - break; - case CompareFunc::Always: - out += "false"; - break; - case CompareFunc::Equal: - case CompareFunc::NotEqual: - case CompareFunc::LessThan: - case CompareFunc::LessThanOrEqual: - case CompareFunc::GreaterThan: - case CompareFunc::GreaterThanOrEqual: { - static const char* op[] = {"!=", "==", ">=", ">", "<=", "<"}; - unsigned index = (unsigned)func - (unsigned)CompareFunc::Equal; - out += "int(last_tex_env_out.a * 255.0) " + std::string(op[index]) + " alphatest_ref"; - break; - } - - default: - out += "false"; - LOG_CRITICAL(Render_OpenGL, "Unknown alpha test condition %u", func); - break; - } -} - -/// 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 TexturingRegs::TevStageConfig>(config.state.tev_stages[index]); - if (!IsPassThroughTevStage(stage)) { - std::string index_name = std::to_string(index); - - out += "vec3 color_results_" + index_name + "[3] = vec3[3]("; - AppendColorModifier(out, config, stage.color_modifier1, stage.color_source1, index_name); - out += ", "; - AppendColorModifier(out, config, stage.color_modifier2, stage.color_source2, index_name); - out += ", "; - AppendColorModifier(out, config, stage.color_modifier3, stage.color_source3, index_name); - out += ");\n"; - - out += "vec3 color_output_" + index_name + " = "; - AppendColorCombiner(out, stage.color_op, "color_results_" + index_name); - out += ";\n"; - - if (stage.color_op == TevStageConfig::Operation::Dot3_RGBA) { - // result of Dot3_RGBA operation is also placed to the alpha component - out += "float alpha_output_" + index_name + " = color_output_" + index_name + "[0];\n"; - } else { - out += "float alpha_results_" + index_name + "[3] = float[3]("; - AppendAlphaModifier(out, config, stage.alpha_modifier1, stage.alpha_source1, - index_name); - out += ", "; - AppendAlphaModifier(out, config, stage.alpha_modifier2, stage.alpha_source2, - index_name); - out += ", "; - AppendAlphaModifier(out, config, stage.alpha_modifier3, stage.alpha_source3, - index_name); - out += ");\n"; - - out += "float alpha_output_" + index_name + " = "; - AppendAlphaCombiner(out, stage.alpha_op, "alpha_results_" + index_name); - out += ";\n"; - } - - out += "last_tex_env_out = vec4(" - "clamp(color_output_" + - index_name + " * " + std::to_string(stage.GetColorMultiplier()) + - ".0, vec3(0.0), vec3(1.0))," - "clamp(alpha_output_" + - index_name + " * " + std::to_string(stage.GetAlphaMultiplier()) + - ".0, 0.0, 1.0));\n"; - } - - out += "combiner_buffer = next_combiner_buffer;\n"; - - if (config.TevStageUpdatesCombinerBufferColor(index)) - out += "next_combiner_buffer.rgb = last_tex_env_out.rgb;\n"; - - if (config.TevStageUpdatesCombinerBufferAlpha(index)) - out += "next_combiner_buffer.a = last_tex_env_out.a;\n"; -} - -/// Writes the code to emulate fragment lighting -static void WriteLighting(std::string& out, const PicaShaderConfig& config) { - const auto& lighting = config.state.lighting; - - // Define lighting globals - out += "vec4 diffuse_sum = vec4(0.0, 0.0, 0.0, 1.0);\n" - "vec4 specular_sum = vec4(0.0, 0.0, 0.0, 1.0);\n" - "vec3 light_vector = vec3(0.0);\n" - "vec3 refl_value = vec3(0.0);\n" - "vec3 spot_dir = vec3(0.0);\n" - "vec3 half_vector = vec3(0.0);\n" - "float geo_factor = 1.0;\n"; - - // Compute fragment normals and tangents - auto Perturbation = [&]() { - return "2.0 * (" + SampleTexture(config, lighting.bump_selector) + ").rgb - 1.0"; - }; - if (lighting.bump_mode == LightingRegs::LightingBumpMode::NormalMap) { - // Bump mapping is enabled using a normal map - out += "vec3 surface_normal = " + Perturbation() + ";\n"; - - // Recompute Z-component of perturbation if 'renorm' is enabled, this provides a higher - // precision result - if (lighting.bump_renorm) { - std::string val = - "(1.0 - (surface_normal.x*surface_normal.x + surface_normal.y*surface_normal.y))"; - out += "surface_normal.z = sqrt(max(" + val + ", 0.0));\n"; - } - - // The tangent vector is not perturbed by the normal map and is just a unit vector. - out += "vec3 surface_tangent = vec3(1.0, 0.0, 0.0);\n"; - } else if (lighting.bump_mode == LightingRegs::LightingBumpMode::TangentMap) { - // Bump mapping is enabled using a tangent map - out += "vec3 surface_tangent = " + Perturbation() + ";\n"; - // Mathematically, recomputing Z-component of the tangent vector won't affect the relevant - // computation below, which is also confirmed on 3DS. So we don't bother recomputing here - // even if 'renorm' is enabled. - - // The normal vector is not perturbed by the tangent map and is just a unit vector. - out += "vec3 surface_normal = vec3(0.0, 0.0, 1.0);\n"; - } else { - // No bump mapping - surface local normal and tangent are just unit vectors - out += "vec3 surface_normal = vec3(0.0, 0.0, 1.0);\n"; - out += "vec3 surface_tangent = vec3(1.0, 0.0, 0.0);\n"; - } - - // Rotate the surface-local normal by the interpolated normal quaternion to convert it to - // eyespace. - out += "vec4 normalized_normquat = normalize(normquat);\n"; - out += "vec3 normal = quaternion_rotate(normalized_normquat, surface_normal);\n"; - out += "vec3 tangent = quaternion_rotate(normalized_normquat, surface_tangent);\n"; - - // Samples the specified lookup table for specular lighting - auto GetLutValue = [&lighting](LightingRegs::LightingSampler sampler, unsigned light_num, - LightingRegs::LightingLutInput input, bool abs) { - std::string index; - switch (input) { - case LightingRegs::LightingLutInput::NH: - index = "dot(normal, normalize(half_vector))"; - break; - - case LightingRegs::LightingLutInput::VH: - index = std::string("dot(normalize(view), normalize(half_vector))"); - break; - - case LightingRegs::LightingLutInput::NV: - index = std::string("dot(normal, normalize(view))"); - break; - - case LightingRegs::LightingLutInput::LN: - index = std::string("dot(light_vector, normal)"); - break; - - case LightingRegs::LightingLutInput::SP: - index = std::string("dot(light_vector, spot_dir)"); - break; - - case LightingRegs::LightingLutInput::CP: - // CP input is only available with configuration 7 - if (lighting.config == LightingRegs::LightingConfig::Config7) { - // Note: even if the normal vector is modified by normal map, which is not the - // normal of the tangent plane anymore, the half angle vector is still projected - // using the modified normal vector. - std::string half_angle_proj = - "normalize(half_vector) - normal * dot(normal, normalize(half_vector))"; - // Note: the half angle vector projection is confirmed not normalized before the dot - // product. The result is in fact not cos(phi) as the name suggested. - index = "dot(" + half_angle_proj + ", tangent)"; - } else { - index = "0.0"; - } - break; - - default: - LOG_CRITICAL(HW_GPU, "Unknown lighting LUT input %d\n", (int)input); - UNIMPLEMENTED(); - index = "0.0"; - break; - } - - std::string sampler_string = std::to_string(static_cast<unsigned>(sampler)); - - 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.0)"; - return "LookupLightingLUTUnsigned(" + sampler_string + ", " + index + ")"; - } else { - // LUT index is in the range of (-1.0, 1.0) - return "LookupLightingLUTSigned(" + sampler_string + ", " + index + ")"; - } - - }; - - // Write the code to emulate each enabled light - for (unsigned light_index = 0; light_index < lighting.src_num; ++light_index) { - const auto& light_config = lighting.light[light_index]; - std::string light_src = "light_src[" + std::to_string(light_config.num) + "]"; - - // Compute light vector (directional or positional) - if (light_config.directional) - out += "light_vector = normalize(" + light_src + ".position);\n"; - else - out += "light_vector = normalize(" + light_src + ".position + view);\n"; - - out += "spot_dir = " + light_src + ".spot_direction;\n"; - out += "half_vector = normalize(view) + light_vector;\n"; - - // Compute dot product of light_vector and normal, adjust if lighting is one-sided or - // two-sided - std::string dot_product = light_config.two_sided_diffuse - ? "abs(dot(light_vector, normal))" - : "max(dot(light_vector, normal), 0.0)"; - - // If enabled, compute spot light attenuation value - std::string spot_atten = "1.0"; - if (light_config.spot_atten_enable && - LightingRegs::IsLightingSamplerSupported( - lighting.config, LightingRegs::LightingSampler::SpotlightAttenuation)) { - std::string value = - GetLutValue(LightingRegs::SpotlightAttenuationSampler(light_config.num), - light_config.num, lighting.lut_sp.type, lighting.lut_sp.abs_input); - spot_atten = "(" + std::to_string(lighting.lut_sp.scale) + " * " + value + ")"; - } - - // If enabled, compute distance attenuation value - std::string dist_atten = "1.0"; - if (light_config.dist_atten_enable) { - std::string index = "clamp(" + light_src + ".dist_atten_scale * length(-view - " + - light_src + ".position) + " + light_src + - ".dist_atten_bias, 0.0, 1.0)"; - auto sampler = LightingRegs::DistanceAttenuationSampler(light_config.num); - dist_atten = "LookupLightingLUTUnsigned(" + - std::to_string(static_cast<unsigned>(sampler)) + "," + index + ")"; - } - - // If enabled, clamp specular component if lighting result is negative - std::string clamp_highlights = - lighting.clamp_highlights ? "(dot(light_vector, normal) <= 0.0 ? 0.0 : 1.0)" : "1.0"; - - if (light_config.geometric_factor_0 || light_config.geometric_factor_1) { - out += "geo_factor = dot(half_vector, half_vector);\n" - "geo_factor = geo_factor == 0.0 ? 0.0 : min(" + - dot_product + " / geo_factor, 1.0);\n"; - } - - // Specular 0 component - std::string d0_lut_value = "1.0"; - if (lighting.lut_d0.enable && - LightingRegs::IsLightingSamplerSupported( - lighting.config, LightingRegs::LightingSampler::Distribution0)) { - // Lookup specular "distribution 0" LUT value - std::string value = - GetLutValue(LightingRegs::LightingSampler::Distribution0, light_config.num, - lighting.lut_d0.type, lighting.lut_d0.abs_input); - d0_lut_value = "(" + std::to_string(lighting.lut_d0.scale) + " * " + value + ")"; - } - std::string specular_0 = "(" + d0_lut_value + " * " + light_src + ".specular_0)"; - if (light_config.geometric_factor_0) { - specular_0 = "(" + specular_0 + " * geo_factor)"; - } - - // If enabled, lookup ReflectRed value, otherwise, 1.0 is used - if (lighting.lut_rr.enable && - LightingRegs::IsLightingSamplerSupported(lighting.config, - LightingRegs::LightingSampler::ReflectRed)) { - std::string value = - GetLutValue(LightingRegs::LightingSampler::ReflectRed, light_config.num, - lighting.lut_rr.type, lighting.lut_rr.abs_input); - value = "(" + std::to_string(lighting.lut_rr.scale) + " * " + value + ")"; - out += "refl_value.r = " + value + ";\n"; - } else { - out += "refl_value.r = 1.0;\n"; - } - - // If enabled, lookup ReflectGreen value, otherwise, ReflectRed value is used - if (lighting.lut_rg.enable && - LightingRegs::IsLightingSamplerSupported(lighting.config, - LightingRegs::LightingSampler::ReflectGreen)) { - std::string value = - GetLutValue(LightingRegs::LightingSampler::ReflectGreen, light_config.num, - lighting.lut_rg.type, lighting.lut_rg.abs_input); - value = "(" + std::to_string(lighting.lut_rg.scale) + " * " + value + ")"; - out += "refl_value.g = " + value + ";\n"; - } else { - out += "refl_value.g = refl_value.r;\n"; - } - - // If enabled, lookup ReflectBlue value, otherwise, ReflectRed value is used - if (lighting.lut_rb.enable && - LightingRegs::IsLightingSamplerSupported(lighting.config, - LightingRegs::LightingSampler::ReflectBlue)) { - std::string value = - GetLutValue(LightingRegs::LightingSampler::ReflectBlue, light_config.num, - lighting.lut_rb.type, lighting.lut_rb.abs_input); - value = "(" + std::to_string(lighting.lut_rb.scale) + " * " + value + ")"; - out += "refl_value.b = " + value + ";\n"; - } else { - out += "refl_value.b = refl_value.r;\n"; - } - - // Specular 1 component - std::string d1_lut_value = "1.0"; - if (lighting.lut_d1.enable && - LightingRegs::IsLightingSamplerSupported( - lighting.config, LightingRegs::LightingSampler::Distribution1)) { - // Lookup specular "distribution 1" LUT value - std::string value = - GetLutValue(LightingRegs::LightingSampler::Distribution1, light_config.num, - lighting.lut_d1.type, lighting.lut_d1.abs_input); - d1_lut_value = "(" + std::to_string(lighting.lut_d1.scale) + " * " + value + ")"; - } - std::string specular_1 = - "(" + d1_lut_value + " * refl_value * " + light_src + ".specular_1)"; - if (light_config.geometric_factor_1) { - specular_1 = "(" + specular_1 + " * geo_factor)"; - } - - // Fresnel - // Note: only the last entry in the light slots applies the Fresnel factor - if (light_index == lighting.src_num - 1 && lighting.lut_fr.enable && - LightingRegs::IsLightingSamplerSupported(lighting.config, - LightingRegs::LightingSampler::Fresnel)) { - // Lookup fresnel LUT value - std::string value = - GetLutValue(LightingRegs::LightingSampler::Fresnel, light_config.num, - lighting.lut_fr.type, lighting.lut_fr.abs_input); - value = "(" + std::to_string(lighting.lut_fr.scale) + " * " + value + ")"; - - // Enabled for diffuse lighting alpha component - 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 == - LightingRegs::LightingFresnelSelector::SecondaryAlpha || - lighting.fresnel_selector == LightingRegs::LightingFresnelSelector::Both) { - out += "specular_sum.a = " + value + ";\n"; - } - } - - // Compute primary fragment color (diffuse lighting) function - out += "diffuse_sum.rgb += ((" + light_src + ".diffuse * " + dot_product + ") + " + - light_src + ".ambient) * " + dist_atten + " * " + spot_atten + ";\n"; - - // Compute secondary fragment color (specular lighting) function - out += "specular_sum.rgb += (" + specular_0 + " + " + specular_1 + ") * " + - clamp_highlights + " * " + dist_atten + " * " + spot_atten + ";\n"; - } - - // Sum final lighting result - out += "diffuse_sum.rgb += lighting_global_ambient;\n"; - out += "primary_fragment_color = clamp(diffuse_sum, vec4(0.0), vec4(1.0));\n"; - out += "secondary_fragment_color = clamp(specular_sum, vec4(0.0), vec4(1.0));\n"; -} - -using ProcTexClamp = TexturingRegs::ProcTexClamp; -using ProcTexShift = TexturingRegs::ProcTexShift; -using ProcTexCombiner = TexturingRegs::ProcTexCombiner; -using ProcTexFilter = TexturingRegs::ProcTexFilter; - -void AppendProcTexShiftOffset(std::string& out, const std::string& v, ProcTexShift mode, - ProcTexClamp clamp_mode) { - std::string offset = (clamp_mode == ProcTexClamp::MirroredRepeat) ? "1.0" : "0.5"; - switch (mode) { - case ProcTexShift::None: - out += "0"; - break; - case ProcTexShift::Odd: - out += offset + " * ((int(" + v + ") / 2) % 2)"; - break; - case ProcTexShift::Even: - out += offset + " * (((int(" + v + ") + 1) / 2) % 2)"; - break; - default: - LOG_CRITICAL(HW_GPU, "Unknown shift mode %u", static_cast<u32>(mode)); - out += "0"; - break; - } -} - -void AppendProcTexClamp(std::string& out, const std::string& var, ProcTexClamp mode) { - switch (mode) { - case ProcTexClamp::ToZero: - out += var + " = " + var + " > 1.0 ? 0 : " + var + ";\n"; - break; - case ProcTexClamp::ToEdge: - out += var + " = " + "min(" + var + ", 1.0);\n"; - break; - case ProcTexClamp::SymmetricalRepeat: - out += var + " = " + "fract(" + var + ");\n"; - break; - case ProcTexClamp::MirroredRepeat: { - out += - var + " = int(" + var + ") % 2 == 0 ? fract(" + var + ") : 1.0 - fract(" + var + ");\n"; - break; - } - case ProcTexClamp::Pulse: - out += var + " = " + var + " > 0.5 ? 1.0 : 0.0;\n"; - break; - default: - LOG_CRITICAL(HW_GPU, "Unknown clamp mode %u", static_cast<u32>(mode)); - out += var + " = " + "min(" + var + ", 1.0);\n"; - break; - } -} - -void AppendProcTexCombineAndMap(std::string& out, ProcTexCombiner combiner, - const std::string& map_lut) { - std::string combined; - switch (combiner) { - case ProcTexCombiner::U: - combined = "u"; - break; - case ProcTexCombiner::U2: - combined = "(u * u)"; - break; - case TexturingRegs::ProcTexCombiner::V: - combined = "v"; - break; - case TexturingRegs::ProcTexCombiner::V2: - combined = "(v * v)"; - break; - case TexturingRegs::ProcTexCombiner::Add: - combined = "((u + v) * 0.5)"; - break; - case TexturingRegs::ProcTexCombiner::Add2: - combined = "((u * u + v * v) * 0.5)"; - break; - case TexturingRegs::ProcTexCombiner::SqrtAdd2: - combined = "min(sqrt(u * u + v * v), 1.0)"; - break; - case TexturingRegs::ProcTexCombiner::Min: - combined = "min(u, v)"; - break; - case TexturingRegs::ProcTexCombiner::Max: - combined = "max(u, v)"; - break; - case TexturingRegs::ProcTexCombiner::RMax: - combined = "min(((u + v) * 0.5 + sqrt(u * u + v * v)) * 0.5, 1.0)"; - break; - default: - LOG_CRITICAL(HW_GPU, "Unknown combiner %u", static_cast<u32>(combiner)); - combined = "0.0"; - break; - } - out += "ProcTexLookupLUT(" + map_lut + ", " + combined + ")"; -} - -void AppendProcTexSampler(std::string& out, const PicaShaderConfig& config) { - // LUT sampling uitlity - // For NoiseLUT/ColorMap/AlphaMap, coord=0.0 is lut[0], coord=127.0/128.0 is lut[127] and - // coord=1.0 is lut[127]+lut_diff[127]. For other indices, the result is interpolated using - // value entries and difference entries. - out += R"( -float ProcTexLookupLUT(samplerBuffer lut, float coord) { - coord *= 128; - float index_i = clamp(floor(coord), 0.0, 127.0); - float index_f = coord - index_i; // fract() cannot be used here because 128.0 needs to be - // extracted as index_i = 127.0 and index_f = 1.0 - vec2 entry = texelFetch(lut, int(index_i)).rg; - return clamp(entry.r + entry.g * index_f, 0.0, 1.0); -} - )"; - - // Noise utility - if (config.state.proctex.noise_enable) { - // See swrasterizer/proctex.cpp for more information about these functions - out += R"( -int ProcTexNoiseRand1D(int v) { - const int table[] = int[](0,4,10,8,4,9,7,12,5,15,13,14,11,15,2,11); - return ((v % 9 + 2) * 3 & 0xF) ^ table[(v / 9) & 0xF]; -} - -float ProcTexNoiseRand2D(vec2 point) { - const int table[] = int[](10,2,15,8,0,7,4,5,5,13,2,6,13,9,3,14); - int u2 = ProcTexNoiseRand1D(int(point.x)); - int v2 = ProcTexNoiseRand1D(int(point.y)); - v2 += ((u2 & 3) == 1) ? 4 : 0; - v2 ^= (u2 & 1) * 6; - v2 += 10 + u2; - v2 &= 0xF; - v2 ^= table[u2]; - return -1.0 + float(v2) * 2.0/ 15.0; -} - -float ProcTexNoiseCoef(vec2 x) { - vec2 grid = 9.0 * proctex_noise_f * abs(x + proctex_noise_p); - vec2 point = floor(grid); - vec2 frac = grid - point; - - float g0 = ProcTexNoiseRand2D(point) * (frac.x + frac.y); - float g1 = ProcTexNoiseRand2D(point + vec2(1.0, 0.0)) * (frac.x + frac.y - 1.0); - float g2 = ProcTexNoiseRand2D(point + vec2(0.0, 1.0)) * (frac.x + frac.y - 1.0); - float g3 = ProcTexNoiseRand2D(point + vec2(1.0, 1.0)) * (frac.x + frac.y - 2.0); - - float x_noise = ProcTexLookupLUT(proctex_noise_lut, frac.x); - float y_noise = ProcTexLookupLUT(proctex_noise_lut, frac.y); - float x0 = mix(g0, g1, x_noise); - float x1 = mix(g2, g3, x_noise); - return mix(x0, x1, y_noise); -} - )"; - } - - out += "vec4 ProcTex() {\n"; - out += "vec2 uv = abs(texcoord[" + std::to_string(config.state.proctex.coord) + "]);\n"; - - // Get shift offset before noise generation - out += "float u_shift = "; - AppendProcTexShiftOffset(out, "uv.y", config.state.proctex.u_shift, - config.state.proctex.u_clamp); - out += ";\n"; - out += "float v_shift = "; - AppendProcTexShiftOffset(out, "uv.x", config.state.proctex.v_shift, - config.state.proctex.v_clamp); - out += ";\n"; - - // Generate noise - if (config.state.proctex.noise_enable) { - out += "uv += proctex_noise_a * ProcTexNoiseCoef(uv);\n"; - out += "uv = abs(uv);\n"; - } - - // Shift - out += "float u = uv.x + u_shift;\n"; - out += "float v = uv.y + v_shift;\n"; - - // Clamp - AppendProcTexClamp(out, "u", config.state.proctex.u_clamp); - AppendProcTexClamp(out, "v", config.state.proctex.v_clamp); - - // Combine and map - out += "float lut_coord = "; - AppendProcTexCombineAndMap(out, config.state.proctex.color_combiner, "proctex_color_map"); - out += ";\n"; - - // Look up color - // For the color lut, coord=0.0 is lut[offset] and coord=1.0 is lut[offset+width-1] - out += "lut_coord *= " + std::to_string(config.state.proctex.lut_width - 1) + ";\n"; - // TODO(wwylele): implement mipmap - switch (config.state.proctex.lut_filter) { - case ProcTexFilter::Linear: - case ProcTexFilter::LinearMipmapLinear: - case ProcTexFilter::LinearMipmapNearest: - out += "int lut_index_i = int(lut_coord) + " + - std::to_string(config.state.proctex.lut_offset) + ";\n"; - out += "float lut_index_f = fract(lut_coord);\n"; - out += "vec4 final_color = texelFetch(proctex_lut, lut_index_i) + lut_index_f * " - "texelFetch(proctex_diff_lut, lut_index_i);\n"; - break; - case ProcTexFilter::Nearest: - case ProcTexFilter::NearestMipmapLinear: - case ProcTexFilter::NearestMipmapNearest: - out += "lut_coord += " + std::to_string(config.state.proctex.lut_offset) + ";\n"; - out += "vec4 final_color = texelFetch(proctex_lut, int(round(lut_coord)));\n"; - break; - } - - if (config.state.proctex.separate_alpha) { - // Note: in separate alpha mode, the alpha channel skips the color LUT look up stage. It - // uses the output of CombineAndMap directly instead. - out += "float final_alpha = "; - AppendProcTexCombineAndMap(out, config.state.proctex.alpha_combiner, "proctex_alpha_map"); - out += ";\n"; - out += "return vec4(final_color.xyz, final_alpha);\n}\n"; - } else { - out += "return final_color;\n}\n"; - } -} - -std::string GenerateFragmentShader(const PicaShaderConfig& config) { - const auto& state = config.state; - - std::string out = R"( -#version 330 core - -in vec4 primary_color; -in vec2 texcoord[3]; -in float texcoord0_w; -in vec4 normquat; -in vec3 view; - -in vec4 gl_FragCoord; - -out vec4 color; - -uniform sampler2D tex[3]; -uniform samplerBuffer lighting_lut; -uniform samplerBuffer fog_lut; -uniform samplerBuffer proctex_noise_lut; -uniform samplerBuffer proctex_color_map; -uniform samplerBuffer proctex_alpha_map; -uniform samplerBuffer proctex_lut; -uniform samplerBuffer proctex_diff_lut; -)"; - - out += UniformBlockDef; - - out += R"( -// Rotate the vector v by the quaternion q -vec3 quaternion_rotate(vec4 q, vec3 v) { - return v + 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v); -} - -float LookupLightingLUT(int lut_index, int index, float delta) { - vec2 entry = texelFetch(lighting_lut, lut_index * 256 + index).rg; - return entry.r + entry.g * delta; -} - -float LookupLightingLUTUnsigned(int lut_index, float pos) { - int index = clamp(int(pos * 256.0), 0, 255); - float delta = pos * 256.0 - index; - return LookupLightingLUT(lut_index, index, delta); -} - -float LookupLightingLUTSigned(int lut_index, float pos) { - int index = clamp(int(pos * 128.0), -128, 127); - float delta = pos * 128.0 - index; - if (index < 0) index += 256; - return LookupLightingLUT(lut_index, index, delta); -} - -)"; - - if (config.state.proctex.enable) - AppendProcTexSampler(out, config); - - out += R"( -void main() { -vec4 primary_fragment_color = vec4(0.0); -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 == FramebufferRegs::CompareFunc::Never) { - out += "discard; }"; - return out; - } - - // Append the scissor test - 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 == RasterizerRegs::ScissorMode::Include) - out += "!"; - out += "(gl_FragCoord.x >= scissor_x1 && " - "gl_FragCoord.y >= scissor_y1 && " - "gl_FragCoord.x < scissor_x2 && " - "gl_FragCoord.y < scissor_y2)) discard;\n"; - } - - // After perspective divide, OpenGL transform z_over_w from [-1, 1] to [near, far]. Here we use - // default near = 0 and far = 1, and undo the transformation to get the original z_over_w, then - // do our own transformation according to PICA specification. - out += "float z_over_w = 2.0 * gl_FragCoord.z - 1.0;\n"; - out += "float depth = z_over_w * depth_scale + depth_offset;\n"; - if (state.depthmap_enable == RasterizerRegs::DepthBuffering::WBuffering) { - out += "depth /= gl_FragCoord.w;\n"; - } - - if (state.lighting.enable) - WriteLighting(out, config); - - out += "vec4 combiner_buffer = vec4(0.0);\n"; - out += "vec4 next_combiner_buffer = tev_combiner_buffer_color;\n"; - out += "vec4 last_tex_env_out = vec4(0.0);\n"; - - for (size_t index = 0; index < state.tev_stages.size(); ++index) - WriteTevStage(out, config, (unsigned)index); - - 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 == TexturingRegs::FogMode::Fog) { - // Get index into fog LUT - if (state.fog_flip) { - out += "float fog_index = (1.0 - depth) * 128.0;\n"; - } else { - out += "float fog_index = depth * 128.0;\n"; - } - - // Generate clamped fog factor from LUT for given fog index - out += "float fog_i = clamp(floor(fog_index), 0.0, 127.0);\n"; - out += "float fog_f = fog_index - fog_i;\n"; - out += "vec2 fog_lut_entry = texelFetch(fog_lut, int(fog_i)).rg;\n"; - out += "float fog_factor = fog_lut_entry.r + fog_lut_entry.g * fog_f;\n"; - out += "fog_factor = clamp(fog_factor, 0.0, 1.0);\n"; - - // Blend the fog - out += "last_tex_env_out.rgb = mix(fog_color.rgb, last_tex_env_out.rgb, fog_factor);\n"; - } else if (state.fog_mode == TexturingRegs::FogMode::Gas) { - Core::Telemetry().AddField(Telemetry::FieldType::Session, "VideoCore_Pica_UseGasMode", - true); - LOG_CRITICAL(Render_OpenGL, "Unimplemented gas mode"); - UNIMPLEMENTED(); - } - - out += "gl_FragDepth = depth;\n"; - out += "color = last_tex_env_out;\n"; - - out += "}"; - - return out; -} - -std::string GenerateVertexShader() { - std::string out = "#version 330 core\n"; - - out += "layout(location = " + std::to_string((int)ATTRIBUTE_POSITION) + - ") in vec4 vert_position;\n"; - out += "layout(location = " + std::to_string((int)ATTRIBUTE_COLOR) + ") in vec4 vert_color;\n"; - out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD0) + - ") in vec2 vert_texcoord0;\n"; - out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD1) + - ") in vec2 vert_texcoord1;\n"; - out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD2) + - ") in vec2 vert_texcoord2;\n"; - out += "layout(location = " + std::to_string((int)ATTRIBUTE_TEXCOORD0_W) + - ") in float vert_texcoord0_w;\n"; - out += "layout(location = " + std::to_string((int)ATTRIBUTE_NORMQUAT) + - ") in vec4 vert_normquat;\n"; - out += "layout(location = " + std::to_string((int)ATTRIBUTE_VIEW) + ") in vec3 vert_view;\n"; - - out += R"( -out vec4 primary_color; -out vec2 texcoord[3]; -out float texcoord0_w; -out vec4 normquat; -out vec3 view; - -)"; - - out += UniformBlockDef; - - out += R"( - -void main() { - primary_color = vert_color; - texcoord[0] = vert_texcoord0; - texcoord[1] = vert_texcoord1; - texcoord[2] = vert_texcoord2; - texcoord0_w = vert_texcoord0_w; - normquat = vert_normquat; - view = vert_view; - gl_Position = vert_position; - gl_ClipDistance[0] = -vert_position.z; // fixed PICA clipping plane z <= 0 - gl_ClipDistance[1] = dot(clip_coef, vert_position); -} -)"; - - return out; -} - -} // namespace GLShader diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h deleted file mode 100644 index 2302ae453..000000000 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <array> -#include <cstring> -#include <functional> -#include <string> -#include <type_traits> -#include "video_core/regs.h" - -namespace GLShader { - -enum Attributes { - ATTRIBUTE_POSITION, - ATTRIBUTE_COLOR, - ATTRIBUTE_TEXCOORD0, - ATTRIBUTE_TEXCOORD1, - ATTRIBUTE_TEXCOORD2, - ATTRIBUTE_TEXCOORD0_W, - ATTRIBUTE_NORMQUAT, - ATTRIBUTE_VIEW, -}; - -/** - * This struct contains all state used to generate the GLSL shader program that emulates the current - * Pica register configuration. This struct is used as a cache key for generated GLSL shader - * programs. The functions in gl_shader_gen.cpp should retrieve state from this struct only, not by - * directly accessing Pica registers. This should reduce the risk of bugs in shader generation where - * Pica state is not being captured in the shader cache key, thereby resulting in (what should be) - * two separate shaders sharing the same key. - * - * We use a union because "implicitly-defined copy/move constructor for a union X copies the object - * representation of X." and "implicitly-defined copy assignment operator for a union X copies the - * object representation (3.9) of X." = Bytewise copy instead of memberwise copy. This is important - * because the padding bytes are included in the hash and comparison between objects. - */ -union PicaShaderConfig { - - /// Construct a PicaShaderConfig with the given Pica register configuration. - static PicaShaderConfig BuildFromRegs(const Pica::Regs& regs); - - bool TevStageUpdatesCombinerBufferColor(unsigned stage_index) const { - return (stage_index < 4) && (state.combiner_buffer_input & (1 << stage_index)); - } - - bool TevStageUpdatesCombinerBufferAlpha(unsigned stage_index) const { - return (stage_index < 4) && ((state.combiner_buffer_input >> 4) & (1 << stage_index)); - } - - bool operator==(const PicaShaderConfig& o) const { - return std::memcmp(&state, &o.state, sizeof(PicaShaderConfig::State)) == 0; - }; - - // NOTE: MSVC15 (Update 2) doesn't think `delete`'d constructors and operators are TC. - // This makes BitField not TC when used in a union or struct so we have to resort - // to this ugly hack. - // Once that bug is fixed we can use Pica::Regs::TevStageConfig here. - // Doesn't include const_color because we don't sync it, see comment in BuildFromRegs() - struct TevStageConfigRaw { - u32 sources_raw; - u32 modifiers_raw; - u32 ops_raw; - u32 scales_raw; - 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; - stage.const_color = 0; - stage.scales_raw = scales_raw; - return stage; - } - }; - - struct State { - Pica::FramebufferRegs::CompareFunc alpha_test_func; - Pica::RasterizerRegs::ScissorMode scissor_test_mode; - Pica::TexturingRegs::TextureConfig::TextureType texture0_type; - bool texture2_use_coord1; - std::array<TevStageConfigRaw, 6> tev_stages; - u8 combiner_buffer_input; - - Pica::RasterizerRegs::DepthBuffering depthmap_enable; - Pica::TexturingRegs::FogMode fog_mode; - bool fog_flip; - - struct { - struct { - unsigned num; - bool directional; - bool two_sided_diffuse; - bool dist_atten_enable; - bool spot_atten_enable; - bool geometric_factor_0; - bool geometric_factor_1; - } light[8]; - - bool enable; - unsigned src_num; - Pica::LightingRegs::LightingBumpMode bump_mode; - unsigned bump_selector; - bool bump_renorm; - bool clamp_highlights; - - Pica::LightingRegs::LightingConfig config; - Pica::LightingRegs::LightingFresnelSelector fresnel_selector; - - struct { - bool enable; - bool abs_input; - Pica::LightingRegs::LightingLutInput type; - float scale; - } lut_d0, lut_d1, lut_sp, lut_fr, lut_rr, lut_rg, lut_rb; - } lighting; - - struct { - bool enable; - u32 coord; - Pica::TexturingRegs::ProcTexClamp u_clamp, v_clamp; - Pica::TexturingRegs::ProcTexCombiner color_combiner, alpha_combiner; - bool separate_alpha; - bool noise_enable; - Pica::TexturingRegs::ProcTexShift u_shift, v_shift; - u32 lut_width; - u32 lut_offset; - Pica::TexturingRegs::ProcTexFilter lut_filter; - } proctex; - - } state; -}; -#if (__GNUC__ >= 5) || defined(__clang__) || defined(_MSC_VER) -static_assert(std::is_trivially_copyable<PicaShaderConfig::State>::value, - "PicaShaderConfig::State must be trivially copyable"); -#endif - -/** - * Generates the GLSL vertex shader program source code for the current Pica state - * @returns String of the shader source code - */ -std::string GenerateVertexShader(); - -/** - * Generates the GLSL fragment shader program source code for the current Pica state - * @param config ShaderCacheKey object generated for the current Pica state, used for the shader - * configuration (NOTE: Use state in this struct only, not the Pica registers!) - * @returns String of the shader source code - */ -std::string GenerateFragmentShader(const PicaShaderConfig& config); - -} // namespace GLShader - -namespace std { -template <> -struct hash<GLShader::PicaShaderConfig> { - size_t operator()(const GLShader::PicaShaderConfig& k) const { - return Common::ComputeHash64(&k.state, sizeof(GLShader::PicaShaderConfig::State)); - } -}; -} // namespace std diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h deleted file mode 100644 index c7fa1f873..000000000 --- a/src/video_core/renderer_opengl/pica_to_gl.h +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <array> -#include <cstddef> -#include <glad/glad.h> -#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 "core/core.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>; -using GLvec4 = std::array<GLfloat, 4>; - -namespace PicaToGL { - -inline GLenum TextureFilterMode(Pica::TexturingRegs::TextureConfig::TextureFilter mode) { - static const GLenum filter_mode_table[] = { - GL_NEAREST, // TextureFilter::Nearest - GL_LINEAR, // TextureFilter::Linear - }; - - // Range check table for input - if (static_cast<size_t>(mode) >= ARRAY_SIZE(filter_mode_table)) { - LOG_CRITICAL(Render_OpenGL, "Unknown texture filtering mode %d", mode); - UNREACHABLE(); - - return GL_LINEAR; - } - - GLenum gl_mode = filter_mode_table[mode]; - - // Check for dummy values indicating an unknown mode - if (gl_mode == 0) { - LOG_CRITICAL(Render_OpenGL, "Unknown texture filtering mode %d", mode); - UNIMPLEMENTED(); - - return GL_LINEAR; - } - - return gl_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 - GL_REPEAT, // WrapMode::Repeat - GL_MIRRORED_REPEAT, // WrapMode::MirroredRepeat - // TODO(wwylele): ClampToEdge2 and ClampToBorder2 are not properly implemented here. See the - // comments in enum WrapMode. - GL_CLAMP_TO_EDGE, // WrapMode::ClampToEdge2 - GL_CLAMP_TO_BORDER, // WrapMode::ClampToBorder2 - GL_REPEAT, // WrapMode::Repeat2 - GL_REPEAT, // WrapMode::Repeat3 - }; - - // Range check table for input - if (static_cast<size_t>(mode) >= ARRAY_SIZE(wrap_mode_table)) { - LOG_CRITICAL(Render_OpenGL, "Unknown texture wrap mode %d", mode); - UNREACHABLE(); - - return GL_CLAMP_TO_EDGE; - } - - if (static_cast<u32>(mode) > 3) { - Core::Telemetry().AddField(Telemetry::FieldType::Session, - "VideoCore_Pica_UnsupportedTextureWrapMode", - static_cast<u32>(mode)); - LOG_WARNING(Render_OpenGL, "Using texture wrap mode %u", static_cast<u32>(mode)); - } - - GLenum gl_mode = wrap_mode_table[mode]; - - // Check for dummy values indicating an unknown mode - if (gl_mode == 0) { - LOG_CRITICAL(Render_OpenGL, "Unknown texture wrap mode %d", mode); - UNIMPLEMENTED(); - - return GL_CLAMP_TO_EDGE; - } - - return gl_mode; -} - -inline GLenum BlendEquation(Pica::FramebufferRegs::BlendEquation equation) { - static const GLenum blend_equation_table[] = { - GL_FUNC_ADD, // BlendEquation::Add - GL_FUNC_SUBTRACT, // BlendEquation::Subtract - GL_FUNC_REVERSE_SUBTRACT, // BlendEquation::ReverseSubtract - GL_MIN, // BlendEquation::Min - GL_MAX, // BlendEquation::Max - }; - - // Range check table for input - if (static_cast<size_t>(equation) >= ARRAY_SIZE(blend_equation_table)) { - LOG_CRITICAL(Render_OpenGL, "Unknown blend equation %d", equation); - UNREACHABLE(); - - return GL_FUNC_ADD; - } - - return blend_equation_table[(unsigned)equation]; -} - -inline GLenum BlendFunc(Pica::FramebufferRegs::BlendFactor factor) { - static const GLenum blend_func_table[] = { - GL_ZERO, // BlendFactor::Zero - GL_ONE, // BlendFactor::One - GL_SRC_COLOR, // BlendFactor::SourceColor - GL_ONE_MINUS_SRC_COLOR, // BlendFactor::OneMinusSourceColor - GL_DST_COLOR, // BlendFactor::DestColor - GL_ONE_MINUS_DST_COLOR, // BlendFactor::OneMinusDestColor - GL_SRC_ALPHA, // BlendFactor::SourceAlpha - GL_ONE_MINUS_SRC_ALPHA, // BlendFactor::OneMinusSourceAlpha - GL_DST_ALPHA, // BlendFactor::DestAlpha - GL_ONE_MINUS_DST_ALPHA, // BlendFactor::OneMinusDestAlpha - GL_CONSTANT_COLOR, // BlendFactor::ConstantColor - GL_ONE_MINUS_CONSTANT_COLOR, // BlendFactor::OneMinusConstantColor - GL_CONSTANT_ALPHA, // BlendFactor::ConstantAlpha - GL_ONE_MINUS_CONSTANT_ALPHA, // BlendFactor::OneMinusConstantAlpha - GL_SRC_ALPHA_SATURATE, // BlendFactor::SourceAlphaSaturate - }; - - // Range check table for input - if (static_cast<size_t>(factor) >= ARRAY_SIZE(blend_func_table)) { - LOG_CRITICAL(Render_OpenGL, "Unknown blend factor %d", factor); - UNREACHABLE(); - - return GL_ONE; - } - - return blend_func_table[(unsigned)factor]; -} - -inline GLenum LogicOp(Pica::FramebufferRegs::LogicOp op) { - static const GLenum logic_op_table[] = { - GL_CLEAR, // Clear - GL_AND, // And - GL_AND_REVERSE, // AndReverse - GL_COPY, // Copy - GL_SET, // Set - GL_COPY_INVERTED, // CopyInverted - GL_NOOP, // NoOp - GL_INVERT, // Invert - GL_NAND, // Nand - GL_OR, // Or - GL_NOR, // Nor - GL_XOR, // Xor - GL_EQUIV, // Equiv - GL_AND_INVERTED, // AndInverted - GL_OR_REVERSE, // OrReverse - GL_OR_INVERTED, // OrInverted - }; - - // Range check table for input - if (static_cast<size_t>(op) >= ARRAY_SIZE(logic_op_table)) { - LOG_CRITICAL(Render_OpenGL, "Unknown logic op %d", op); - UNREACHABLE(); - - return GL_COPY; - } - - return logic_op_table[(unsigned)op]; -} - -inline GLenum CompareFunc(Pica::FramebufferRegs::CompareFunc func) { - static const GLenum compare_func_table[] = { - GL_NEVER, // CompareFunc::Never - GL_ALWAYS, // CompareFunc::Always - GL_EQUAL, // CompareFunc::Equal - GL_NOTEQUAL, // CompareFunc::NotEqual - GL_LESS, // CompareFunc::LessThan - GL_LEQUAL, // CompareFunc::LessThanOrEqual - GL_GREATER, // CompareFunc::GreaterThan - GL_GEQUAL, // CompareFunc::GreaterThanOrEqual - }; - - // Range check table for input - if (static_cast<size_t>(func) >= ARRAY_SIZE(compare_func_table)) { - LOG_CRITICAL(Render_OpenGL, "Unknown compare function %d", func); - UNREACHABLE(); - - return GL_ALWAYS; - } - - return compare_func_table[(unsigned)func]; -} - -inline GLenum StencilOp(Pica::FramebufferRegs::StencilAction action) { - static const GLenum stencil_op_table[] = { - GL_KEEP, // StencilAction::Keep - GL_ZERO, // StencilAction::Zero - GL_REPLACE, // StencilAction::Replace - GL_INCR, // StencilAction::Increment - GL_DECR, // StencilAction::Decrement - GL_INVERT, // StencilAction::Invert - GL_INCR_WRAP, // StencilAction::IncrementWrap - GL_DECR_WRAP, // StencilAction::DecrementWrap - }; - - // Range check table for input - if (static_cast<size_t>(action) >= ARRAY_SIZE(stencil_op_table)) { - LOG_CRITICAL(Render_OpenGL, "Unknown stencil op %d", action); - UNREACHABLE(); - - return GL_KEEP; - } - - return stencil_op_table[(unsigned)action]; -} - -inline GLvec4 ColorRGBA8(const u32 color) { - return {{ - (color >> 0 & 0xFF) / 255.0f, (color >> 8 & 0xFF) / 255.0f, (color >> 16 & 0xFF) / 255.0f, - (color >> 24 & 0xFF) / 255.0f, - }}; -} - -inline std::array<GLfloat, 3> LightColor(const Pica::LightingRegs::LightColor& color) { - return {{ - color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, - }}; -} - -} // namespace diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index e59eb7d76..410b0e959 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -13,14 +13,11 @@ #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" #include "core/hw/lcd.h" #include "core/memory.h" #include "core/settings.h" #include "core/tracer/recorder.h" -#include "video_core/debug_utils/debug_utils.h" -#include "video_core/rasterizer_interface.h" #include "video_core/renderer_opengl/renderer_opengl.h" #include "video_core/video_core.h" @@ -128,10 +125,6 @@ void RendererOpenGL::SwapBuffers(const FramebufferInfo& framebuffer_info) { prev_state.Apply(); RefreshRasterizerSetting(); - - if (Pica::g_debug_context && Pica::g_debug_context->recorder) { - Pica::g_debug_context->recorder->FrameFinished(); - } } static inline u32 MortonInterleave128(u32 x, u32 y) { diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 9d2bb8423..dc21d7a38 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -8,7 +8,6 @@ #include <glad/glad.h> #include "common/common_types.h" #include "common/math_util.h" -#include "core/hw/gpu.h" #include "video_core/renderer_base.h" #include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_state.h" |