From 15e8664ef739c878aafc6c889c6db4fad1c69849 Mon Sep 17 00:00:00 2001 From: wwylele Date: Fri, 9 Mar 2018 16:46:34 +0200 Subject: [PATCH 1/2] gl_rasterizer: implement texture cube --- .../renderer_opengl/gl_rasterizer.cpp | 29 ++++++++ .../renderer_opengl/gl_rasterizer.h | 4 ++ .../renderer_opengl/gl_rasterizer_cache.cpp | 71 ++++++++++++++++++- .../renderer_opengl/gl_rasterizer_cache.h | 7 +- .../renderer_opengl/gl_shader_gen.cpp | 3 + src/video_core/renderer_opengl/gl_state.cpp | 16 +++++ src/video_core/renderer_opengl/gl_state.h | 6 ++ 7 files changed, 134 insertions(+), 2 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index f028ea0019..9cbd7196c5 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -40,6 +40,12 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) { state.texture_units[i].sampler = texture_samplers[i].sampler.handle; } + // Create cubemap texture and sampler objects + texture_cube_sampler.Create(); + state.texture_cube_unit.sampler = texture_cube_sampler.sampler.handle; + texture_cube.Create(); + state.texture_cube_unit.texture_cube = texture_cube.handle; + // Generate VBO, VAO and UBO vertex_buffer.Create(); vertex_array.Create(); @@ -352,6 +358,25 @@ void RasterizerOpenGL::DrawTriangles() { const auto& texture = pica_textures[texture_index]; if (texture.enabled) { + if (texture_index == 0) { + using TextureType = Pica::TexturingRegs::TextureConfig::TextureType; + switch (texture.config.type.Value()) { + case TextureType::TextureCube: + using CubeFace = Pica::TexturingRegs::CubeFace; + res_cache.FillTextureCube( + texture_cube.handle, texture, + regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveX), + regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeX), + regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveY), + regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeY), + regs.texturing.GetCubePhysicalAddress(CubeFace::PositiveZ), + regs.texturing.GetCubePhysicalAddress(CubeFace::NegativeZ)); + texture_cube_sampler.SyncWithConfig(texture.config); + state.texture_units[texture_index].texture_2d = 0; + continue; // Texture unit 0 setup finished. Continue to next unit + } + } + texture_samplers[texture_index].SyncWithConfig(texture.config); Surface surface = res_cache.GetTextureSurface(texture); if (surface != nullptr) { @@ -1225,6 +1250,10 @@ void RasterizerOpenGL::SetShader() { if (uniform_tex != -1) { glUniform1i(uniform_tex, TextureUnits::PicaTexture(2).id); } + uniform_tex = glGetUniformLocation(shader->shader.handle, "tex_cube"); + if (uniform_tex != -1) { + glUniform1i(uniform_tex, TextureUnits::TextureCube.id); + } // Set the texture samplers to correspond to different lookup table texture units GLint uniform_lut = glGetUniformLocation(shader->shader.handle, "lighting_lut"); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 18808b1e4e..72af50f118 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -287,6 +287,10 @@ private: OGLBuffer uniform_buffer; OGLFramebuffer framebuffer; + // TODO (wwylele): consider caching texture cube in the rasterizer cache + OGLTexture texture_cube; + SamplerInfo texture_cube_sampler; + OGLBuffer lighting_lut_buffer; OGLTexture lighting_lut; std::array, Pica::LightingRegs::NumLightingSampler> lighting_lut_data{}; diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index aef06873d8..81ba557d96 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include +#include #include #include #include @@ -1192,10 +1193,14 @@ SurfaceRect_Tuple RasterizerCacheOpenGL::GetSurfaceSubRect(const SurfaceParams& } Surface RasterizerCacheOpenGL::GetTextureSurface( - const Pica::TexturingRegs::FullTextureConfig& config) { + const Pica::TexturingRegs::FullTextureConfig& config, PAddr addr_override) { Pica::Texture::TextureInfo info = Pica::Texture::TextureInfo::FromPicaRegister(config.config, config.format); + if (addr_override != 0) { + info.physical_address = addr_override; + } + SurfaceParams params; params.addr = info.physical_address; params.width = info.width; @@ -1223,6 +1228,70 @@ Surface RasterizerCacheOpenGL::GetTextureSurface( return GetSurface(params, ScaleMatch::Ignore, true); } +void RasterizerCacheOpenGL::FillTextureCube(GLuint dest_handle, + const Pica::TexturingRegs::FullTextureConfig& config, + PAddr px, PAddr nx, PAddr py, PAddr ny, PAddr pz, + PAddr nz) { + ASSERT(config.config.width == config.config.height); + struct FaceTuple { + PAddr address; + GLenum gl_face; + Surface surface; + }; + std::array faces{{ + {px, GL_TEXTURE_CUBE_MAP_POSITIVE_X}, + {nx, GL_TEXTURE_CUBE_MAP_NEGATIVE_X}, + {py, GL_TEXTURE_CUBE_MAP_POSITIVE_Y}, + {ny, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y}, + {pz, GL_TEXTURE_CUBE_MAP_POSITIVE_Z}, + {nz, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z}, + }}; + + u16 res_scale = 1; + for (auto& face : faces) { + face.surface = GetTextureSurface(config, face.address); + res_scale = std::max(res_scale, face.surface->res_scale); + } + + u32 scaled_size = res_scale * config.config.width; + + OpenGLState state = OpenGLState::GetCurState(); + + OpenGLState prev_state = state; + SCOPE_EXIT({ prev_state.Apply(); }); + + state.texture_cube_unit.texture_cube = dest_handle; + state.Apply(); + glActiveTexture(TextureUnits::TextureCube.Enum()); + FormatTuple format_tuple = GetFormatTuple(faces[0].surface->pixel_format); + for (auto& face : faces) { + glTexImage2D(face.gl_face, 0, format_tuple.internal_format, scaled_size, scaled_size, 0, + format_tuple.format, format_tuple.type, nullptr); + } + + state.draw.read_framebuffer = read_framebuffer.handle; + state.draw.draw_framebuffer = draw_framebuffer.handle; + state.ResetTexture(dest_handle); + + for (auto& face : faces) { + state.ResetTexture(face.surface->texture.handle); + state.Apply(); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + face.surface->texture.handle, 0); + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, + 0); + + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, face.gl_face, dest_handle, + 0); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, + 0); + + auto src_rect = face.surface->GetScaledRect(); + glBlitFramebuffer(src_rect.left, src_rect.bottom, src_rect.right, src_rect.top, 0, 0, + scaled_size, scaled_size, GL_COLOR_BUFFER_BIT, GL_LINEAR); + } +} + SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces( bool using_color_fb, bool using_depth_fb, const MathUtil::Rectangle& viewport_rect) { const auto& regs = Pica::g_state.regs; diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 7ef6a94985..719f713a13 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -322,7 +322,12 @@ public: bool load_if_create); /// Get a surface based on the texture configuration - Surface GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config); + Surface GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config, + PAddr addr_override = 0); + + /// Copy surfaces to a cubemap texture based on the texture configuration + void FillTextureCube(GLuint dest_handle, const Pica::TexturingRegs::FullTextureConfig& config, + PAddr px, PAddr nx, PAddr py, PAddr ny, PAddr pz, PAddr nz); /// Get the color and depth surfaces based on the framebuffer configuration SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb, diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index a9f7ecbfe7..04ffa926bb 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -202,6 +202,8 @@ static std::string SampleTexture(const PicaShaderConfig& config, unsigned textur return "texture(tex[0], texcoord[0])"; case TexturingRegs::TextureConfig::Projection2D: return "textureProj(tex[0], vec3(texcoord[0], texcoord0_w))"; + case TexturingRegs::TextureConfig::TextureCube: + return "texture(tex_cube, vec3(texcoord[0], texcoord0_w))"; default: LOG_CRITICAL(HW_GPU, "Unhandled texture type %x", static_cast(state.texture0_type)); @@ -1060,6 +1062,7 @@ in vec4 gl_FragCoord; out vec4 color; uniform sampler2D tex[3]; +uniform samplerCube tex_cube; uniform samplerBuffer lighting_lut; uniform samplerBuffer fog_lut; uniform samplerBuffer proctex_noise_lut; diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 76354b8422..efb302da22 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -52,6 +52,9 @@ OpenGLState::OpenGLState() { texture_unit.sampler = 0; } + texture_cube_unit.texture_cube = 0; + texture_cube_unit.sampler = 0; + lighting_lut.texture_buffer = 0; fog_lut.texture_buffer = 0; @@ -201,6 +204,14 @@ void OpenGLState::Apply() const { } } + if (texture_cube_unit.texture_cube != cur_state.texture_cube_unit.texture_cube) { + glActiveTexture(TextureUnits::TextureCube.Enum()); + glBindTexture(GL_TEXTURE_CUBE_MAP, texture_cube_unit.texture_cube); + } + if (texture_cube_unit.sampler != cur_state.texture_cube_unit.sampler) { + glBindSampler(TextureUnits::TextureCube.id, texture_cube_unit.sampler); + } + // Lighting LUTs if (lighting_lut.texture_buffer != cur_state.lighting_lut.texture_buffer) { glActiveTexture(TextureUnits::LightingLUT.Enum()); @@ -311,6 +322,8 @@ OpenGLState& OpenGLState::ResetTexture(GLuint handle) { unit.texture_2d = 0; } } + if (texture_cube_unit.texture_cube == handle) + texture_cube_unit.texture_cube = 0; if (lighting_lut.texture_buffer == handle) lighting_lut.texture_buffer = 0; if (fog_lut.texture_buffer == handle) @@ -334,6 +347,9 @@ OpenGLState& OpenGLState::ResetSampler(GLuint handle) { unit.sampler = 0; } } + if (texture_cube_unit.sampler == handle) { + texture_cube_unit.sampler = 0; + } return *this; } diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 033d417bce..a8a2f1e7d1 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -27,6 +27,7 @@ constexpr TextureUnit ProcTexColorMap{6}; constexpr TextureUnit ProcTexAlphaMap{7}; constexpr TextureUnit ProcTexLUT{8}; constexpr TextureUnit ProcTexDiffLUT{9}; +constexpr TextureUnit TextureCube{10}; } // namespace TextureUnits @@ -87,6 +88,11 @@ public: GLuint sampler; // GL_SAMPLER_BINDING } texture_units[3]; + struct { + GLuint texture_cube; // GL_TEXTURE_BINDING_CUBE_MAP + GLuint sampler; // GL_SAMPLER_BINDING + } texture_cube_unit; + struct { GLuint texture_buffer; // GL_TEXTURE_BINDING_BUFFER } lighting_lut; From 889d8aaab3e8d7370f1f14e846ec06b3b601b0fb Mon Sep 17 00:00:00 2001 From: wwylele Date: Sun, 11 Mar 2018 13:31:29 +0200 Subject: [PATCH 2/2] gl_rasterizer/cache: only reallocate cubemap when size/format mismatch --- .../renderer_opengl/gl_rasterizer_cache.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 81ba557d96..1500519fcd 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -1264,9 +1264,17 @@ void RasterizerCacheOpenGL::FillTextureCube(GLuint dest_handle, state.Apply(); glActiveTexture(TextureUnits::TextureCube.Enum()); FormatTuple format_tuple = GetFormatTuple(faces[0].surface->pixel_format); - for (auto& face : faces) { - glTexImage2D(face.gl_face, 0, format_tuple.internal_format, scaled_size, scaled_size, 0, - format_tuple.format, format_tuple.type, nullptr); + + GLint cur_size, cur_format; + glGetTexLevelParameteriv(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_TEXTURE_WIDTH, &cur_size); + glGetTexLevelParameteriv(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_TEXTURE_INTERNAL_FORMAT, + &cur_format); + + if (cur_size != scaled_size || cur_format != format_tuple.internal_format) { + for (auto& face : faces) { + glTexImage2D(face.gl_face, 0, format_tuple.internal_format, scaled_size, scaled_size, 0, + format_tuple.format, format_tuple.type, nullptr); + } } state.draw.read_framebuffer = read_framebuffer.handle;