diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 7152f0716..d2db44629 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -242,13 +242,37 @@ void RasterizerOpenGL::DrawTriangles() { const bool using_depth_fb = regs.framebuffer.framebuffer.GetDepthBufferPhysicalAddress() != 0 && (write_depth_fb || state.depth.test_enabled || (has_stencil && state.stencil.test_enabled)); - // Sync and bind the framebuffer surfaces + MathUtil::Rectangle viewport_rect_unscaled{ + // These registers hold half-width and half-height, so must be multiplied by 2 + regs.rasterizer.viewport_corner.x, // left + regs.rasterizer.viewport_corner.y + // top + static_cast(Pica::float24::FromRaw(regs.rasterizer.viewport_size_y).ToFloat32() * 2), + regs.rasterizer.viewport_corner.x + // right + static_cast(Pica::float24::FromRaw(regs.rasterizer.viewport_size_x).ToFloat32() * 2), + regs.rasterizer.viewport_corner.y // bottom + }; + Surface color_surface; Surface depth_surface; - MathUtil::Rectangle rect; - std::tie(color_surface, depth_surface, rect) = - res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb); + MathUtil::Rectangle surfaces_rect; + std::tie(color_surface, depth_surface, surfaces_rect) = + res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb, viewport_rect_unscaled); + const u16 res_scale = color_surface != nullptr ? color_surface->res_scale : + (depth_surface == nullptr ? 1u : depth_surface->res_scale); + + MathUtil::Rectangle draw_rect{ + MathUtil::Clamp(surfaces_rect.left + viewport_rect_unscaled.left * res_scale, // left + surfaces_rect.left, surfaces_rect.right), + MathUtil::Clamp(surfaces_rect.bottom + viewport_rect_unscaled.GetHeight() * res_scale, // top + surfaces_rect.bottom, surfaces_rect.top), + MathUtil::Clamp(surfaces_rect.left + viewport_rect_unscaled.GetWidth() * res_scale, // right + surfaces_rect.left, surfaces_rect.right), + MathUtil::Clamp(surfaces_rect.bottom + viewport_rect_unscaled.bottom * res_scale, // bottom + surfaces_rect.bottom, surfaces_rect.top) + }; + + // Bind the framebuffer surfaces state.draw.draw_framebuffer = framebuffer.handle; state.Apply(); @@ -273,20 +297,11 @@ void RasterizerOpenGL::DrawTriangles() { } // Sync the viewport - // These registers hold half-width and half-height, so must be multiplied by 2 - const GLsizei viewport_width = - static_cast(Pica::float24::FromRaw(regs.rasterizer.viewport_size_x).ToFloat32() * 2); - const GLsizei viewport_height = - static_cast(Pica::float24::FromRaw(regs.rasterizer.viewport_size_y).ToFloat32() * 2); - - const u16 res_scale = color_surface != nullptr ? color_surface->res_scale : - (depth_surface == nullptr ? 1u : depth_surface->res_scale); - glViewport( - static_cast(rect.left + regs.rasterizer.viewport_corner.x * res_scale), - static_cast(rect.bottom + regs.rasterizer.viewport_corner.y * res_scale), - viewport_width * res_scale, - viewport_height * res_scale); + static_cast(surfaces_rect.left + viewport_rect_unscaled.left * res_scale), + static_cast(surfaces_rect.bottom + viewport_rect_unscaled.bottom * res_scale), + static_cast(viewport_rect_unscaled.GetWidth() * res_scale), + static_cast(viewport_rect_unscaled.GetHeight() * res_scale)); if (uniform_block_data.data.framebuffer_scale != res_scale) { uniform_block_data.data.framebuffer_scale = res_scale; @@ -296,15 +311,15 @@ void RasterizerOpenGL::DrawTriangles() { // Scissor checks are window-, not viewport-relative, which means that if the cached texture // sub-rect changes, the scissor bounds also need to be updated. GLint scissor_x1 = static_cast( - rect.left + regs.rasterizer.scissor_test.x1 * res_scale); + surfaces_rect.left + regs.rasterizer.scissor_test.x1 * res_scale); GLint scissor_y1 = static_cast( - rect.bottom + regs.rasterizer.scissor_test.y1 * res_scale); + surfaces_rect.bottom + regs.rasterizer.scissor_test.y1 * res_scale); // 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( - rect.left + (regs.rasterizer.scissor_test.x2 + 1) * res_scale); + surfaces_rect.left + (regs.rasterizer.scissor_test.x2 + 1) * res_scale); GLint scissor_y2 = static_cast( - rect.bottom + (regs.rasterizer.scissor_test.y2 + 1) * res_scale); + surfaces_rect.bottom + (regs.rasterizer.scissor_test.y2 + 1) * res_scale); if (uniform_block_data.data.scissor_x1 != scissor_x1 || uniform_block_data.data.scissor_x2 != scissor_x2 || @@ -394,6 +409,15 @@ void RasterizerOpenGL::DrawTriangles() { uniform_block_data.dirty = false; } + // Viewport can have negative offsets or larger + // dimensions than our framebuffer sub-rect. + // Enable scissor test to prevent drawing + // outside of the framebuffer region + state.scissor.enabled = true; + state.scissor.x = draw_rect.left; + state.scissor.y = draw_rect.bottom; + state.scissor.width = draw_rect.GetWidth(); + state.scissor.height = draw_rect.GetHeight(); state.Apply(); // Draw the vertex batch @@ -401,29 +425,8 @@ void RasterizerOpenGL::DrawTriangles() { GL_STREAM_DRAW); glDrawArrays(GL_TRIANGLES, 0, (GLsizei)vertex_batch.size()); - // Mark framebuffer surfaces as dirty - const u32 viewport_offset = - ((regs.framebuffer.framebuffer.GetHeight() - regs.rasterizer.viewport_corner.y - viewport_height) - * regs.framebuffer.framebuffer.GetWidth()) - + regs.rasterizer.viewport_corner.x; - - const u32 viewport_size = ((viewport_height - 1) * regs.framebuffer.framebuffer.GetWidth()) - + viewport_width; - - if (color_surface != nullptr && write_color_fb) { - res_cache.InvalidateRegion( - regs.framebuffer.framebuffer.GetColorBufferPhysicalAddress() - + (viewport_offset * color_surface->bytes_per_pixel), - viewport_size * color_surface->bytes_per_pixel, - color_surface); - } - if (depth_surface != nullptr && write_depth_fb) { - res_cache.InvalidateRegion( - regs.framebuffer.framebuffer.GetDepthBufferPhysicalAddress() - + (viewport_offset * depth_surface->bytes_per_pixel), - viewport_size * depth_surface->bytes_per_pixel, - depth_surface); - } + // Disable scissor test + state.scissor.enabled = false; vertex_batch.clear(); @@ -432,6 +435,25 @@ void RasterizerOpenGL::DrawTriangles() { state.texture_units[texture_index].texture_2d = 0; } state.Apply(); + + // Mark framebuffer surfaces as dirty + MathUtil::Rectangle draw_rect_unscaled{ + draw_rect.left / res_scale, draw_rect.top / res_scale, + draw_rect.right / res_scale, draw_rect.bottom / res_scale + }; + + if (color_surface != nullptr && write_color_fb) { + auto interval = color_surface->GetSubRectInterval(draw_rect_unscaled); + res_cache.InvalidateRegion(boost::icl::first(interval), + boost::icl::length(interval), + color_surface); + } + if (depth_surface != nullptr && write_depth_fb) { + auto interval = depth_surface->GetSubRectInterval(draw_rect_unscaled); + res_cache.InvalidateRegion(boost::icl::first(interval), + boost::icl::length(interval), + depth_surface); + } } void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 972818d93..4ee164ae0 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -11,6 +11,7 @@ #include #include #include +#include "common/alignment.h" #include "common/bit_field.h" #include "common/color.h" #include "common/logging/log.h" @@ -312,7 +313,26 @@ static bool FillSurface(const Surface& surface, const u8* fill_data) { return true; } -MathUtil::Rectangle CachedSurface::GetSubRect(const SurfaceParams& sub_surface) const { +SurfaceInterval SurfaceParams::GetSubRectInterval(MathUtil::Rectangle unscaled_rect) const { + if (unscaled_rect.top > unscaled_rect.bottom) { + std::swap(unscaled_rect.top, unscaled_rect.bottom); + } + if (is_tiled) { + unscaled_rect.left = Common::AlignDown(unscaled_rect.left, 8); + unscaled_rect.top = Common::AlignDown(unscaled_rect.top, 8); + unscaled_rect.right = Common::AlignUp(unscaled_rect.right, 8); + unscaled_rect.bottom = Common::AlignUp(unscaled_rect.bottom, 8); + } + + const u32 pixel_offset = unscaled_rect.left + stride * + (!is_tiled ? unscaled_rect.top : height - unscaled_rect.top - unscaled_rect.GetHeight()); + + const u32 pixels = (unscaled_rect.GetHeight() - 1) * stride + unscaled_rect.GetWidth(); + + return { addr + BytesInPixels(pixel_offset), addr + BytesInPixels(pixel_offset + pixels) }; +} + +MathUtil::Rectangle SurfaceParams::GetSubRect(const SurfaceParams& sub_surface) const { const u32 begin_pixel_index = PixelsInBytes(sub_surface.addr - addr); const int x0 = begin_pixel_index % stride; const int y0 = begin_pixel_index / stride; @@ -323,7 +343,7 @@ MathUtil::Rectangle CachedSurface::GetSubRect(const SurfaceParams& sub_surf return MathUtil::Rectangle(x0, y0, x0 + sub_surface.width, y0 + sub_surface.height); // Top to bottom } -MathUtil::Rectangle CachedSurface::GetScaledSubRect(const SurfaceParams& sub_surface) const { +MathUtil::Rectangle SurfaceParams::GetScaledSubRect(const SurfaceParams& sub_surface) const { auto rect = GetSubRect(sub_surface); rect.left = rect.left * res_scale; rect.right = rect.right * res_scale; @@ -332,7 +352,7 @@ MathUtil::Rectangle CachedSurface::GetScaledSubRect(const SurfaceParams& su return rect; } -bool CachedSurface::ExactMatch(const SurfaceParams& other_surface) const { +bool SurfaceParams::ExactMatch(const SurfaceParams& other_surface) const { return (other_surface.addr == addr && other_surface.width == width && other_surface.height == height && @@ -341,7 +361,7 @@ bool CachedSurface::ExactMatch(const SurfaceParams& other_surface) const { other_surface.is_tiled == is_tiled); } -bool CachedSurface::CanSubRect(const SurfaceParams& sub_surface) const { +bool SurfaceParams::CanSubRect(const SurfaceParams& sub_surface) const { if (sub_surface.addr < addr || sub_surface.end > end || sub_surface.stride != stride || sub_surface.pixel_format != pixel_format || sub_surface.is_tiled != is_tiled) return false; @@ -357,36 +377,7 @@ bool CachedSurface::CanSubRect(const SurfaceParams& sub_surface) const { return true; } -bool CachedSurface::CanCopy(const SurfaceParams& dest_surface) const { - if (type == SurfaceType::Fill && IsRegionValid(dest_surface.GetInterval()) && - dest_surface.addr >= addr && dest_surface.end <= end) { // dest_surface is within our fill range - if (fill_size != dest_surface.bytes_per_pixel) { - if (dest_surface.is_tiled && GetFormatBpp(dest_surface.pixel_format) * 8 % fill_size != 0) - return false; - - // Check if bits repeat for our fill_size - const u32 dest_bytes_per_pixel = std::max(dest_surface.bytes_per_pixel, 1u); // Take care of 4bpp formats - std::vector fill_test(fill_size * dest_bytes_per_pixel); - - for (u32 i = 0; i < dest_bytes_per_pixel; ++i) - std::memcpy(&fill_test[i * fill_size], &fill_data[0], fill_size); - - for (u32 i = 0; i < fill_size; ++i) - if (std::memcmp(&fill_test[dest_bytes_per_pixel * i], &fill_test[0], dest_bytes_per_pixel) != 0) - return false; - - if (dest_surface.bytes_per_pixel == 0 && (fill_test[0] & 0xF) != (fill_test[0] >> 4)) // 4bpp compare - return false; - } - return true; - } - if (CanSubRect(dest_surface) && dest_surface.width == stride) - return true; - - return false; -} - -bool CachedSurface::CanExpand(const SurfaceParams& expanded_surface) const { +bool SurfaceParams::CanExpand(const SurfaceParams& expanded_surface) const { if (pixel_format != expanded_surface.pixel_format || is_tiled != expanded_surface.is_tiled || addr > expanded_surface.end || expanded_surface.addr > end || @@ -402,7 +393,7 @@ bool CachedSurface::CanExpand(const SurfaceParams& expanded_surface) const { return x0 == 0 && (!is_tiled || y0 % 8 == 0); } -bool CachedSurface::CanTexCopy(const SurfaceParams& texcopy_params) const { +bool SurfaceParams::CanTexCopy(const SurfaceParams& texcopy_params) const { // TODO: Accept "Fill" surfaces if (pixel_format == PixelFormat::Invalid || addr > texcopy_params.addr || end < texcopy_params.end || @@ -425,6 +416,35 @@ bool CachedSurface::CanTexCopy(const SurfaceParams& texcopy_params) const { x0 + PixelsInBytes(texcopy_params.width / 8) <= stride); } +bool CachedSurface::CanCopy(const SurfaceParams& dest_surface) const { + if (type == SurfaceType::Fill && IsRegionValid(dest_surface.GetInterval()) && + dest_surface.addr >= addr && dest_surface.end <= end) { // dest_surface is within our fill range + if (fill_size != dest_surface.BytesPerPixel()) { + if (dest_surface.is_tiled && BytesInPixels(8 * 8) % fill_size != 0) + return false; + + // Check if bits repeat for our fill_size + const u32 dest_bytes_per_pixel = std::max(dest_surface.BytesPerPixel(), 1u); // Take care of 4bpp formats + std::vector fill_test(fill_size * dest_bytes_per_pixel); + + for (u32 i = 0; i < dest_bytes_per_pixel; ++i) + std::memcpy(&fill_test[i * fill_size], &fill_data[0], fill_size); + + for (u32 i = 0; i < fill_size; ++i) + if (std::memcmp(&fill_test[dest_bytes_per_pixel * i], &fill_test[0], dest_bytes_per_pixel) != 0) + return false; + + if (dest_surface.BytesPerPixel() == 0 && (fill_test[0] & 0xF) != (fill_test[0] >> 4)) // 4bpp compare + return false; + } + return true; + } + if (CanSubRect(dest_surface) && dest_surface.width == stride) + return true; + + return false; +} + static void CopySurface(const Surface& src_surface, const Surface& dest_surface) { if (src_surface == dest_surface) return; @@ -492,7 +512,7 @@ void CachedSurface::LoadGLBuffer(PAddr load_start, PAddr load_end) { } else { size_t copyfn_offset = MortonCopyFlags::MortonToGl; - copyfn_offset |= (bytes_per_pixel - 1) << MortonCopyFlags::BytesPerPixelBits; + copyfn_offset |= (BytesPerPixel() - 1) << MortonCopyFlags::BytesPerPixelBits; copyfn_offset |= (gl_bytes_per_pixel - 1) << MortonCopyFlags::GLBytesPerPixelBits; if (load_start != addr || load_end != end) @@ -544,7 +564,7 @@ void CachedSurface::FlushGLBuffer(PAddr flush_start, PAddr flush_end) { std::memcpy(dst_buffer + start_offset, &gl_buffer[start_offset], flush_end - flush_start); } else { - size_t copyfn_offset = (bytes_per_pixel - 1) << MortonCopyFlags::BytesPerPixelBits; + size_t copyfn_offset = (BytesPerPixel() - 1) << MortonCopyFlags::BytesPerPixelBits; copyfn_offset |= (gl_bytes_per_pixel - 1) << MortonCopyFlags::GLBytesPerPixelBits; if (flush_start != addr || flush_end != end) @@ -806,7 +826,7 @@ SurfaceRect_Tuple RasterizerCacheOpenGL::GetSurfaceSubRect(const SurfaceParams& new_params.addr = std::min(params.addr, surface->addr); new_params.end = std::max(params.end, surface->end); new_params.size = new_params.end - new_params.addr; - new_params.height = new_params.size / (SurfaceParams::GetFormatBpp(params.pixel_format) * params.stride / 8); + new_params.height = new_params.size / params.BytesInPixels(params.stride); Surface new_surface = CreateSurface(new_params); RegisterSurface(new_surface); @@ -861,30 +881,12 @@ constexpr u16 GetResolutionScaleFactor() { Settings::values.resolution_factor; } -SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(bool using_color_fb, - bool using_depth_fb) { +SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces( + bool using_color_fb, bool using_depth_fb, + const MathUtil::Rectangle& viewport_rect) { const auto& regs = Pica::g_state.regs; const auto& config = regs.framebuffer.framebuffer; - // 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 = using_color_fb && using_depth_fb && - 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)); - - if (framebuffers_overlap) { - LOG_CRITICAL(Render_OpenGL, "Color and depth framebuffer memory regions overlap; overlapping framebuffers not supported!"); - using_depth_fb = false; - } - - // get color and depth surfaces - SurfaceParams color_params; - SurfaceParams depth_params; - color_params.is_tiled = depth_params.is_tiled = true; - // update resolution_scale_factor and reset cache if changed static u16 resolution_scale_factor = GetResolutionScaleFactor(); if (resolution_scale_factor != GetResolutionScaleFactor()) { @@ -893,38 +895,63 @@ SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(bool usin InvalidateRegion(0, 0xffffffff, nullptr); } - color_params.addr = config.GetColorBufferPhysicalAddress(); - color_params.width = depth_params.width = config.GetWidth(); - color_params.height = depth_params.height = config.GetHeight(); - color_params.pixel_format = SurfaceParams::PixelFormatFromColorFormat(config.color_format); + MathUtil::Rectangle viewport_clamped{ + static_cast(MathUtil::Clamp(viewport_rect.left, 0, static_cast(config.GetWidth()))), + static_cast(MathUtil::Clamp(viewport_rect.top, 0, static_cast(config.GetHeight()))), + static_cast(MathUtil::Clamp(viewport_rect.right, 0, static_cast(config.GetWidth()))), + static_cast(MathUtil::Clamp(viewport_rect.bottom, 0, static_cast(config.GetHeight()))) + }; + + // get color and depth surfaces + SurfaceParams color_params; + color_params.is_tiled = true; color_params.res_scale = resolution_scale_factor; + color_params.width = config.GetWidth(); + color_params.height = config.GetHeight(); + SurfaceParams depth_params = color_params; + + color_params.addr = config.GetColorBufferPhysicalAddress(); + color_params.pixel_format = SurfaceParams::PixelFormatFromColorFormat(config.color_format); color_params.UpdateParams(); + depth_params.addr = config.GetDepthBufferPhysicalAddress(); + depth_params.pixel_format = SurfaceParams::PixelFormatFromDepthFormat(config.depth_format); + depth_params.UpdateParams(); + + auto color_vp_interval = color_params.GetSubRectInterval(viewport_clamped); + auto depth_vp_interval = depth_params.GetSubRectInterval(viewport_clamped); + + // Make sur that framebuffers don't overlap if both color and depth are being used + if (using_color_fb && using_depth_fb && + boost::icl::length(color_vp_interval & depth_vp_interval)) { + LOG_CRITICAL(Render_OpenGL, "Color and depth framebuffer memory regions overlap; overlapping framebuffers not supported!"); + using_depth_fb = false; + } + MathUtil::Rectangle rect{}; Surface color_surface = nullptr; - if (using_color_fb) - std::tie(color_surface, rect) = GetSurfaceSubRect(color_params, ScaleMatch::Exact, true); - - depth_params.pixel_format = SurfaceParams::PixelFormatFromDepthFormat(config.depth_format); - depth_params.addr = config.GetDepthBufferPhysicalAddress(); - depth_params.res_scale = resolution_scale_factor; - depth_params.UpdateParams(); - Surface depth_surface = nullptr; - if (using_depth_fb && color_surface != nullptr) { - const PAddr validate_addr = depth_params.addr; - const u32 validate_size = depth_params.size; + if (using_color_fb) + std::tie(color_surface, rect) = GetSurfaceSubRect(color_params, ScaleMatch::Exact, false); + if (using_depth_fb && color_surface != nullptr) { // Can't specify separate color and depth viewport offsets in OpenGL, so make sure depth_surface will have the same offsets - depth_params.addr -= color_surface->PixelsInBytes(color_params.addr - color_surface->addr) * depth_params.bytes_per_pixel; + depth_params.addr -= depth_params.BytesInPixels( + color_surface->PixelsInBytes(color_params.addr - color_surface->addr)); depth_params.height = color_surface->height; depth_params.UpdateParams(); depth_surface = GetSurface(depth_params, ScaleMatch::Exact, false); - ValidateSurface(depth_surface, validate_addr, validate_size); } else if (using_depth_fb) { - std::tie(depth_surface, rect) = GetSurfaceSubRect(depth_params, ScaleMatch::Exact, true); + std::tie(depth_surface, rect) = GetSurfaceSubRect(depth_params, ScaleMatch::Exact, false); + } + + if (color_surface != nullptr) { + ValidateSurface(color_surface, boost::icl::first(color_vp_interval), boost::icl::length(color_vp_interval)); + } + if (depth_surface != nullptr) { + ValidateSurface(depth_surface, boost::icl::first(depth_vp_interval), boost::icl::length(depth_vp_interval)); } return { color_surface, depth_surface, rect }; @@ -1003,14 +1030,14 @@ void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, PAddr addr, SurfaceParams params = *surface; const u32 pixel_offset = params.PixelsInBytes(interval_start - params.addr); if (!params.is_tiled) { - params.addr += (pixel_offset - (pixel_offset % params.width)) * - SurfaceParams::GetFormatBpp(params.pixel_format) / 8; // Start of the row - params.height = (params.PixelsInBytes(interval_end - params.addr - 1) / params.width) + 1; + // Start of the row + params.addr += params.BytesInPixels(pixel_offset - (pixel_offset % params.stride)); + params.height = (params.PixelsInBytes(interval_end - params.addr - 1) / params.stride) + 1; } else { - params.addr += (pixel_offset - (pixel_offset % (params.width * 8))) * - SurfaceParams::GetFormatBpp(params.pixel_format) / 8; // Start of the tiled row - params.height = ((params.PixelsInBytes(interval_end - params.addr - 1) / (params.width * 8)) + 1) * 8; + // Start of the tiled row + params.addr += params.BytesInPixels(pixel_offset - (pixel_offset % (params.stride * 8))); + params.height = ((params.PixelsInBytes(interval_end - params.addr - 1) / (params.stride * 8)) + 1) * 8; } params.UpdateParams(); @@ -1147,7 +1174,7 @@ Surface RasterizerCacheOpenGL::CreateSurface(const SurfaceParams& params) { surface->gl_bytes_per_pixel = (surface->pixel_format == PixelFormat::D24 || surface->type == SurfaceType::Texture) ? 4 : - surface->bytes_per_pixel; + surface->BytesPerPixel(); surface->gl_buffer_offset = (surface->pixel_format == PixelFormat::D24) ? 1 : 0; diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 56cdfb0de..0b3cc5c09 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -179,7 +179,7 @@ struct SurfaceParams { return SurfaceType::Invalid; } - /// Update the params "size", "end", "bytes_per_pixel" and "type" from the already set "addr", "width", "height" and "pixel_format" + /// Update the params "size", "end" and "type" from the already set "addr", "width", "height" and "pixel_format" void UpdateParams() { size = width * height * GetFormatBpp(pixel_format) / 8; @@ -190,13 +190,14 @@ struct SurfaceParams { end = addr + size; type = GetFormatType(pixel_format); - bytes_per_pixel = GetFormatBpp(pixel_format) / 8; } SurfaceInterval GetInterval() const { return SurfaceInterval::right_open(addr, end); } + SurfaceInterval GetSubRectInterval(MathUtil::Rectangle unscaled_rect) const; + u32 GetScaledWidth() const { return width * res_scale; } @@ -217,6 +218,22 @@ struct SurfaceParams { return size * 8 / GetFormatBpp(pixel_format); } + u32 BytesInPixels(u32 pixels) const { + return pixels * GetFormatBpp(pixel_format) / 8; + } + + u32 BytesPerPixel() const { + return BytesInPixels(1); + } + + bool ExactMatch(const SurfaceParams& other_surface) const; + bool CanSubRect(const SurfaceParams& sub_surface) const; + bool CanExpand(const SurfaceParams& expanded_surface) const; + bool CanTexCopy(const SurfaceParams& texcopy_params) const; + + MathUtil::Rectangle GetSubRect(const SurfaceParams& sub_surface) const; + MathUtil::Rectangle GetScaledSubRect(const SurfaceParams& sub_surface) const; + PAddr addr = 0; PAddr end = 0; u32 size = 0; @@ -227,20 +244,12 @@ struct SurfaceParams { u16 res_scale = 1; bool is_tiled = false; - u32 bytes_per_pixel = 0; PixelFormat pixel_format = PixelFormat::Invalid; SurfaceType type = SurfaceType::Invalid; }; struct CachedSurface : SurfaceParams { - bool ExactMatch(const SurfaceParams& other_surface) const; - bool CanSubRect(const SurfaceParams& sub_surface) const; bool CanCopy(const SurfaceParams& dest_surface) const; - bool CanExpand(const SurfaceParams& expanded_surface) const; - bool CanTexCopy(const SurfaceParams& texcopy_params) const; - - MathUtil::Rectangle GetSubRect(const SurfaceParams& sub_surface) const; - MathUtil::Rectangle GetScaledSubRect(const SurfaceParams& sub_surface) const; bool IsRegionValid(const SurfaceInterval& interval) const { return (invalid_regions.find(interval) == invalid_regions.end()); @@ -295,7 +304,8 @@ public: Surface GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config); /// Get the color and depth surfaces based on the framebuffer configuration - SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb); + SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb, + const MathUtil::Rectangle& viewport_rect); /// Get a surface that matches the fill config Surface GetFillSurface(const GPU::Regs::MemoryFillConfig& config); diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 06a905766..93c6477e1 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -69,6 +69,12 @@ OpenGLState::OpenGLState() { draw.uniform_buffer = 0; draw.shader_program = 0; + scissor.enabled = false; + scissor.x = 0; + scissor.y = 0; + scissor.width = 0; + scissor.height = 0; + clip_distance = {}; } @@ -263,6 +269,22 @@ void OpenGLState::Apply() const { glUseProgram(draw.shader_program); } + // Scissor test + if (scissor.enabled != cur_state.scissor.enabled) { + if (scissor.enabled) { + glEnable(GL_SCISSOR_TEST); + } else { + glDisable(GL_SCISSOR_TEST); + } + } + + if (scissor.x != cur_state.scissor.x || + scissor.y != cur_state.scissor.y || + scissor.width != cur_state.scissor.width || + scissor.height != cur_state.scissor.height) { + glScissor(scissor.x, scissor.y, scissor.width, scissor.height); + } + // Clip distance for (size_t i = 0; i < clip_distance.size(); ++i) { if (clip_distance[i] != cur_state.clip_distance[i]) { diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 437fe34c4..a6bd9476e 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -124,6 +124,14 @@ public: GLuint shader_program; // GL_CURRENT_PROGRAM } draw; + struct { + bool enabled; // GL_SCISSOR_TEST + GLint x; + GLint y; + GLsizei width; + GLsizei height; + } scissor; + std::array clip_distance; // GL_CLIP_DISTANCE OpenGLState();