From 5627955900bfbbb44b49513eaa88eee7b69dbad7 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Sun, 6 Oct 2019 13:09:03 +1000 Subject: [PATCH] GPU: Two-pass rendering for B-F transparency --- src/core/gpu_hw.cpp | 19 +++++++++-- src/core/gpu_hw.h | 12 +++++-- src/core/gpu_hw_opengl.cpp | 67 +++++++++++++++++++------------------- src/core/gpu_hw_opengl.h | 10 +++--- src/duckstation/main.cpp | 4 +-- 5 files changed, 65 insertions(+), 47 deletions(-) diff --git a/src/core/gpu_hw.cpp b/src/core/gpu_hw.cpp index 14593adbb..18f5e9a6a 100644 --- a/src/core/gpu_hw.cpp +++ b/src/core/gpu_hw.cpp @@ -245,12 +245,14 @@ void main() return ss.str(); } -std::string GPU_HW::GenerateFragmentShader(bool transparent, bool textured, TextureColorMode texture_color_mode, - bool blending) +std::string GPU_HW::GenerateFragmentShader(TransparencyRenderMode transparency, bool textured, + TextureColorMode texture_color_mode, bool blending) { std::stringstream ss; GenerateShaderHeader(ss); - DefineMacro(ss, "TRANSPARENT", transparent); + DefineMacro(ss, "TRANSPARENT", transparency != TransparencyRenderMode::Off); + DefineMacro(ss, "TRANSPARENT_ONLY_OPAQUE", transparency == TransparencyRenderMode::OnlyOpaque); + DefineMacro(ss, "TRANSPARENT_ONLY_TRANSPARENT", transparency == TransparencyRenderMode::OnlyTransparent); DefineMacro(ss, "TEXTURED", textured); DefineMacro(ss, "PALETTE", textured && (texture_color_mode == GPU::TextureColorMode::Palette4Bit || @@ -337,9 +339,19 @@ void main() #if TRANSPARENT // Apply semitransparency. If not a semitransparent texel, destination alpha is ignored. if (texcol.a != 0) + { + #if TRANSPARENT_ONLY_OPAQUE + discard; + #endif o_col0 = vec4(color * u_transparent_alpha.x, u_transparent_alpha.y); + } else + { + #if TRANSPARENT_ONLY_TRANSPARENT + discard; + #endif o_col0 = vec4(color, 0.0); + } #else // Mask bit from texture. o_col0 = vec4(color, texcol.a); @@ -575,4 +587,5 @@ void GPU_HW::DispatchRenderCommand(RenderCommand rc, u32 num_vertices) } LoadVertices(rc, num_vertices); + FlushRender(); } diff --git a/src/core/gpu_hw.h b/src/core/gpu_hw.h index 51e279eaa..d4c32d21a 100644 --- a/src/core/gpu_hw.h +++ b/src/core/gpu_hw.h @@ -56,6 +56,14 @@ protected: std::vector vertices; }; + enum class TransparencyRenderMode + { + Off, + TransparentAndOpaque, + OnlyOpaque, + OnlyTransparent + }; + static constexpr u32 VERTEX_BUFFER_SIZE = 1 * 1024 * 1024; static constexpr u32 MAX_BATCH_VERTEX_COUNT = VERTEX_BUFFER_SIZE / sizeof(HWVertex); static constexpr u32 TEXTURE_TILE_SIZE = 256; @@ -85,8 +93,8 @@ protected: } std::string GenerateVertexShader(bool textured); - std::string GenerateFragmentShader(bool transparent, bool textured, TextureColorMode texture_color_mode, - bool blending); + std::string GenerateFragmentShader(TransparencyRenderMode transparency, bool textured, + TextureColorMode texture_color_mode, bool blending); std::string GenerateScreenQuadVertexShader(); std::string GenerateFillFragmentShader(); std::string GenerateRGB24DecodeFragmentShader(); diff --git a/src/core/gpu_hw_opengl.cpp b/src/core/gpu_hw_opengl.cpp index d63d01c84..87593ea3f 100644 --- a/src/core/gpu_hw_opengl.cpp +++ b/src/core/gpu_hw_opengl.cpp @@ -61,9 +61,6 @@ void GPU_HW_OpenGL::RestoreGraphicsAPIState() glLineWidth(static_cast(m_resolution_scale)); UpdateDrawingArea(); - m_last_transparency_enable = false; - glDisable(GL_BLEND); - glBindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer); glBindVertexArray(m_vao_id); } @@ -291,17 +288,17 @@ void GPU_HW_OpenGL::CreateVertexBuffer() bool GPU_HW_OpenGL::CompilePrograms() { - for (u32 textured = 0; textured < 2; textured++) + for (u32 transparent = 0; transparent < 4; transparent++) { - for (u32 blending = 0; blending < 2; blending++) + for (u32 textured = 0; textured < 2; textured++) { - for (u32 transparent = 0; transparent < 2; transparent++) + for (u32 format = 0; format < 3; format++) { - for (u32 format = 0; format < 3; format++) + for (u32 blending = 0; blending < 2; blending++) { // TODO: eliminate duplicate shaders here if (!CompileProgram(m_render_programs[transparent][textured][format][blending], - ConvertToBoolUnchecked(transparent), ConvertToBoolUnchecked(textured), + static_cast(transparent), ConvertToBoolUnchecked(textured), static_cast(format), ConvertToBoolUnchecked(blending))) { return false; @@ -329,7 +326,7 @@ bool GPU_HW_OpenGL::CompilePrograms() return true; } -bool GPU_HW_OpenGL::CompileProgram(GL::Program& prog, bool transparent, bool textured, +bool GPU_HW_OpenGL::CompileProgram(GL::Program& prog, TransparencyRenderMode transparent, bool textured, TextureColorMode texture_color_mode, bool blending) { const std::string vs = GenerateVertexShader(textured); @@ -366,10 +363,10 @@ bool GPU_HW_OpenGL::CompileProgram(GL::Program& prog, bool transparent, bool tex return true; } -void GPU_HW_OpenGL::SetDrawState() +void GPU_HW_OpenGL::SetDrawState(TransparencyRenderMode render_mode) { const GL::Program& prog = - m_render_programs[BoolToUInt32(m_batch.transparency_enable)][BoolToUInt32(m_batch.texture_enable)] + m_render_programs[static_cast(render_mode)][BoolToUInt32(m_batch.texture_enable)] [static_cast(m_batch.texture_color_mode)][BoolToUInt32(m_batch.texture_blending_enable)]; prog.Bind(); @@ -392,25 +389,17 @@ void GPU_HW_OpenGL::SetDrawState() m_vram_read_texture->Bind(); } - if (m_last_transparency_enable != m_batch.transparency_enable || - (m_last_transparency_enable && m_last_transparency_mode != m_batch.transparency_mode)) + if (render_mode == TransparencyRenderMode::Off || render_mode == TransparencyRenderMode::OnlyOpaque) { - m_last_transparency_enable = m_batch.texture_enable; - m_last_transparency_mode = m_batch.transparency_mode; - - if (!m_batch.transparency_enable) - { - glDisable(GL_BLEND); - } - else - { - glEnable(GL_BLEND); - glBlendEquationSeparate(m_batch.transparency_mode == GPU::TransparencyMode::BackgroundMinusForeground ? - GL_FUNC_REVERSE_SUBTRACT : - GL_FUNC_ADD, - GL_FUNC_ADD); - glBlendFuncSeparate(GL_ONE, GL_SRC_ALPHA, GL_ONE, GL_ZERO); - } + glDisable(GL_BLEND); + } + else + { + glEnable(GL_BLEND); + glBlendEquationSeparate( + m_batch.transparency_mode == TransparencyMode::BackgroundMinusForeground ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD, + GL_FUNC_ADD); + glBlendFuncSeparate(GL_ONE, GL_SRC_ALPHA, GL_ONE, GL_ZERO); } if (m_drawing_area_changed) @@ -472,8 +461,6 @@ void GPU_HW_OpenGL::UpdateDisplay() glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_vram_fbo); glViewport(0, 0, m_vram_texture->GetWidth(), m_vram_texture->GetHeight()); glEnable(GL_SCISSOR_TEST); - if (m_last_transparency_enable) - glEnable(GL_BLEND); } else { @@ -662,14 +649,26 @@ void GPU_HW_OpenGL::FlushRender() m_stats.num_batches++; m_stats.num_vertices += static_cast(m_batch.vertices.size()); - SetDrawState(); - Assert((m_batch.vertices.size() * sizeof(HWVertex)) <= VERTEX_BUFFER_SIZE); glBufferSubData(GL_ARRAY_BUFFER, 0, static_cast(sizeof(HWVertex) * m_batch.vertices.size()), m_batch.vertices.data()); static constexpr std::array gl_primitives = {{GL_LINES, GL_LINE_STRIP, GL_TRIANGLES, GL_TRIANGLE_STRIP}}; - glDrawArrays(gl_primitives[static_cast(m_batch.primitive)], 0, static_cast(m_batch.vertices.size())); + + if (m_batch.transparency_enable && m_batch.texture_enable && + m_batch.transparency_mode == GPU::TransparencyMode::BackgroundMinusForeground) + { + SetDrawState(TransparencyRenderMode::OnlyTransparent); + glDrawArrays(gl_primitives[static_cast(m_batch.primitive)], 0, static_cast(m_batch.vertices.size())); + SetDrawState(TransparencyRenderMode::OnlyOpaque); + glDrawArrays(gl_primitives[static_cast(m_batch.primitive)], 0, static_cast(m_batch.vertices.size())); + } + else + { + SetDrawState(m_batch.transparency_enable ? TransparencyRenderMode::TransparentAndOpaque : + TransparencyRenderMode::Off); + glDrawArrays(gl_primitives[static_cast(m_batch.primitive)], 0, static_cast(m_batch.vertices.size())); + } m_batch.vertices.clear(); } diff --git a/src/core/gpu_hw_opengl.h b/src/core/gpu_hw_opengl.h index a3fb9bd95..d48745d05 100644 --- a/src/core/gpu_hw_opengl.h +++ b/src/core/gpu_hw_opengl.h @@ -51,9 +51,9 @@ private: void CreateVertexBuffer(); bool CompilePrograms(); - bool CompileProgram(GL::Program& prog, bool transparent, bool textured, TextureColorMode texture_color_mode, - bool blending); - void SetDrawState(); + bool CompileProgram(GL::Program& prog, TransparencyRenderMode transparent, bool textured, + TextureColorMode texture_color_mode, bool blending); + void SetDrawState(TransparencyRenderMode render_mode); // downsample texture - used for readbacks at >1xIR. std::unique_ptr m_vram_texture; @@ -71,11 +71,9 @@ private: GLuint m_attributeless_vao_id = 0; bool m_vram_read_texture_dirty = true; - bool m_last_transparency_enable = false; - TransparencyMode m_last_transparency_mode = TransparencyMode::BackgroundMinusForeground; bool m_drawing_area_changed = true; - std::array, 3>, 2>, 2> m_render_programs; + std::array, 3>, 2>, 4> m_render_programs; GL::Program m_reinterpret_rgb8_program; GLStats m_stats = {}; diff --git a/src/duckstation/main.cpp b/src/duckstation/main.cpp index b78f8c32c..2a89900e4 100644 --- a/src/duckstation/main.cpp +++ b/src/duckstation/main.cpp @@ -94,9 +94,9 @@ int main(int argc, char* argv[]) g_pLog->SetConsoleOutputParams(true, "Pad SPU", level); g_pLog->SetFilterLevel(level); #else - // g_pLog->SetConsoleOutputParams(true, nullptr, LOGLEVEL_DEBUG); + g_pLog->SetConsoleOutputParams(true, nullptr, LOGLEVEL_DEBUG); // g_pLog->SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL SPU Pad DigitalController", LOGLEVEL_DEBUG); - g_pLog->SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL SPU Pad DigitalController InterruptController", LOGLEVEL_DEBUG); + // g_pLog->SetConsoleOutputParams(true, "GPU GPU_HW_OpenGL SPU Pad DigitalController InterruptController", LOGLEVEL_DEBUG); // g_pLog->SetFilterLevel(LOGLEVEL_TRACE); g_pLog->SetFilterLevel(LOGLEVEL_DEBUG); // g_pLog->SetFilterLevel(LOGLEVEL_DEV);