diff --git a/src/video_core/rasterizer_cache/rasterizer_cache.h b/src/video_core/rasterizer_cache/rasterizer_cache.h index 8546a64a9..7248fd4bd 100644 --- a/src/video_core/rasterizer_cache/rasterizer_cache.h +++ b/src/video_core/rasterizer_cache/rasterizer_cache.h @@ -374,43 +374,30 @@ void RasterizerCache::CopySurface(const Surface& src_surface, const Surface& SurfaceInterval copy_interval) { MICROPROFILE_SCOPE(RasterizerCache_CopySurface); - const SurfaceParams subrect_params = dst_surface->FromInterval(copy_interval); + const PAddr copy_addr = copy_interval.lower(); + const auto subrect_params = dst_surface->FromInterval(copy_interval); + const Rect2D dst_rect = dst_surface->GetScaledSubRect(subrect_params); ASSERT(subrect_params.GetInterval() == copy_interval && src_surface != dst_surface); if (src_surface->type == SurfaceType::Fill) { - // FillSurface needs a 4 bytes buffer - const u32 fill_offset = - (boost::icl::first(copy_interval) - src_surface->addr) % src_surface->fill_size; - std::array fill_buffer; - - u32 fill_buff_pos = fill_offset; - for (std::size_t i = 0; i < fill_buffer.size(); i++) { - fill_buffer[i] = src_surface->fill_data[fill_buff_pos++ % src_surface->fill_size]; - } - - const ClearValue clear_value = - MakeClearValue(dst_surface->type, dst_surface->pixel_format, fill_buffer.data()); - const TextureClear clear_rect = { - .texture_level = 0, .texture_rect = dst_surface->GetScaledSubRect(subrect_params)}; - - runtime.ClearTexture(*dst_surface, clear_rect, clear_value); - return; - } - - if (src_surface->CanSubRect(subrect_params)) { - const TextureBlit texture_blit = { - .src_level = 0, - .dst_level = 0, - .src_layer = 0, - .dst_layer = 0, - .src_rect = src_surface->GetScaledSubRect(subrect_params), - .dst_rect = dst_surface->GetScaledSubRect(subrect_params), + const TextureClear texture_clear = { + .texture_level = 0, + .texture_rect = dst_rect, + .value = src_surface->MakeClearValue(copy_addr, dst_surface->pixel_format), }; - runtime.BlitTextures(*src_surface, *dst_surface, texture_blit); + runtime.ClearTexture(*dst_surface, texture_clear); return; } - UNREACHABLE(); + const TextureBlit texture_blit = { + .src_level = 0, + .dst_level = 0, + .src_layer = 0, + .dst_layer = 0, + .src_rect = src_surface->GetScaledSubRect(subrect_params), + .dst_rect = dst_rect, + }; + runtime.BlitTextures(*src_surface, *dst_surface, texture_blit); } template diff --git a/src/video_core/rasterizer_cache/surface_base.cpp b/src/video_core/rasterizer_cache/surface_base.cpp index 726b8a997..4a9580f43 100644 --- a/src/video_core/rasterizer_cache/surface_base.cpp +++ b/src/video_core/rasterizer_cache/surface_base.cpp @@ -5,6 +5,7 @@ #include "common/alignment.h" #include "common/assert.h" #include "video_core/rasterizer_cache/surface_base.h" +#include "video_core/texture/texture_decode.h" namespace VideoCore { @@ -98,6 +99,58 @@ SurfaceInterval SurfaceBase::GetCopyableInterval(const SurfaceParams& params) co return result; } +ClearValue SurfaceBase::MakeClearValue(PAddr copy_addr, PixelFormat dst_format) { + const SurfaceType dst_type = GetFormatType(dst_format); + const std::array fill_buffer = MakeFillBuffer(copy_addr); + + ClearValue result{}; + switch (dst_type) { + case SurfaceType::Color: + case SurfaceType::Texture: + case SurfaceType::Fill: { + Pica::Texture::TextureInfo tex_info{}; + tex_info.format = static_cast(dst_format); + const auto color = Pica::Texture::LookupTexture(fill_buffer.data(), 0, 0, tex_info); + result.color = color / 255.f; + break; + } + case SurfaceType::Depth: { + u32 depth_uint = 0; + if (dst_format == PixelFormat::D16) { + std::memcpy(&depth_uint, fill_buffer.data(), 2); + result.depth = depth_uint / 65535.0f; // 2^16 - 1 + } else if (dst_format == PixelFormat::D24) { + std::memcpy(&depth_uint, fill_buffer.data(), 3); + result.depth = depth_uint / 16777215.0f; // 2^24 - 1 + } + break; + } + case SurfaceType::DepthStencil: { + u32 clear_value_uint; + std::memcpy(&clear_value_uint, fill_buffer.data(), sizeof(u32)); + result.depth = (clear_value_uint & 0xFFFFFF) / 16777215.0f; // 2^24 - 1 + result.stencil = (clear_value_uint >> 24); + break; + } + default: + UNREACHABLE_MSG("Invalid surface type!"); + } + + return result; +} + +std::array SurfaceBase::MakeFillBuffer(PAddr copy_addr) { + const PAddr fill_offset = (copy_addr - addr) % fill_size; + std::array fill_buffer; + + u32 fill_buff_pos = fill_offset; + for (std::size_t i = 0; i < fill_buffer.size(); i++) { + fill_buffer[i] = fill_data[fill_buff_pos++ % fill_size]; + } + + return fill_buffer; +} + std::shared_ptr SurfaceBase::CreateWatcher() { auto weak_ptr = weak_from_this(); auto watcher = std::make_shared(std::move(weak_ptr)); diff --git a/src/video_core/rasterizer_cache/surface_base.h b/src/video_core/rasterizer_cache/surface_base.h index 0595158ff..b00cb2b75 100644 --- a/src/video_core/rasterizer_cache/surface_base.h +++ b/src/video_core/rasterizer_cache/surface_base.h @@ -63,6 +63,9 @@ public: /// Returns the region of the biggest valid rectange within interval SurfaceInterval GetCopyableInterval(const SurfaceParams& params) const; + /// Returns the clear value used to validate another surface from this fill surface + ClearValue MakeClearValue(PAddr copy_addr, PixelFormat dst_format); + /// Creates a surface watcher linked to this surface std::shared_ptr CreateWatcher(); @@ -83,6 +86,10 @@ public: return *invalid_regions.equal_range(interval).first == interval; } +private: + /// Returns the fill buffer value starting from copy_addr + std::array MakeFillBuffer(PAddr copy_addr); + public: bool registered = false; bool picked = false; diff --git a/src/video_core/rasterizer_cache/utils.h b/src/video_core/rasterizer_cache/utils.h index 0bfd997c7..0c59dd057 100644 --- a/src/video_core/rasterizer_cache/utils.h +++ b/src/video_core/rasterizer_cache/utils.h @@ -48,6 +48,7 @@ union ClearValue { struct TextureClear { u32 texture_level; Rect2D texture_rect; + ClearValue value; }; struct TextureCopy { @@ -76,12 +77,6 @@ struct BufferTextureCopy { u32 texture_level; }; -struct BufferCopy { - u32 src_offset; - u32 dst_offset; - u32 size; -}; - struct HostTextureTag { PixelFormat format{}; TextureType type{}; diff --git a/src/video_core/renderer_opengl/gl_texture_runtime.cpp b/src/video_core/renderer_opengl/gl_texture_runtime.cpp index 342ad3199..345951860 100644 --- a/src/video_core/renderer_opengl/gl_texture_runtime.cpp +++ b/src/video_core/renderer_opengl/gl_texture_runtime.cpp @@ -158,8 +158,7 @@ OGLTexture TextureRuntime::Allocate(u32 width, u32 height, u32 levels, return texture; } -bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClear& clear, - VideoCore::ClearValue value) { +bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClear& clear) { OpenGLState prev_state = OpenGLState::GetCurState(); SCOPE_EXIT({ prev_state.Apply(); }); @@ -189,7 +188,7 @@ bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClea state.color_mask.alpha_enabled = true; state.Apply(); - glClearBufferfv(GL_COLOR, 0, value.color.AsArray()); + glClearBufferfv(GL_COLOR, 0, clear.value.color.AsArray()); break; case VideoCore::SurfaceType::Depth: glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); @@ -200,7 +199,7 @@ bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClea state.depth.write_mask = GL_TRUE; state.Apply(); - glClearBufferfv(GL_DEPTH, 0, &value.depth); + glClearBufferfv(GL_DEPTH, 0, &clear.value.depth); break; case VideoCore::SurfaceType::DepthStencil: glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); @@ -211,7 +210,7 @@ bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClea state.stencil.write_mask = -1; state.Apply(); - glClearBufferfi(GL_DEPTH_STENCIL, 0, value.depth, value.stencil); + glClearBufferfi(GL_DEPTH_STENCIL, 0, clear.value.depth, clear.value.stencil); break; default: UNREACHABLE_MSG("Invalid surface type!"); diff --git a/src/video_core/renderer_opengl/gl_texture_runtime.h b/src/video_core/renderer_opengl/gl_texture_runtime.h index feddd74a0..93fe45fd8 100644 --- a/src/video_core/renderer_opengl/gl_texture_runtime.h +++ b/src/video_core/renderer_opengl/gl_texture_runtime.h @@ -57,8 +57,7 @@ public: VideoCore::TextureType type); /// Fills the rectangle of the texture with the clear value provided - bool ClearTexture(Surface& surface, const VideoCore::TextureClear& clear, - VideoCore::ClearValue value); + bool ClearTexture(Surface& surface, const VideoCore::TextureClear& clear); /// Copies a rectangle of src_tex to another rectange of dst_rect bool CopyTextures(Surface& source, Surface& dest, const VideoCore::TextureCopy& copy); diff --git a/src/video_core/renderer_vulkan/vk_texture_runtime.cpp b/src/video_core/renderer_vulkan/vk_texture_runtime.cpp index 8f16c420b..870804b19 100644 --- a/src/video_core/renderer_vulkan/vk_texture_runtime.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_runtime.cpp @@ -306,8 +306,7 @@ void TextureRuntime::Recycle(const HostTextureTag tag, ImageAlloc&& alloc) { texture_recycler.emplace(tag, std::move(alloc)); } -bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClear& clear, - VideoCore::ClearValue value) { +bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClear& clear) { renderpass_cache.ExitRenderpass(); const RecordParams params = { @@ -318,7 +317,7 @@ bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClea }; if (clear.texture_rect == surface.GetScaledRect()) { - scheduler.Record([params, clear, value](vk::CommandBuffer cmdbuf) { + scheduler.Record([params, clear](vk::CommandBuffer cmdbuf) { const vk::ImageSubresourceRange range = { .aspectMask = params.aspect, .baseMipLevel = clear.texture_level, @@ -368,11 +367,11 @@ bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClea static_cast(params.aspect & vk::ImageAspectFlagBits::eColor); if (is_color) { cmdbuf.clearColorImage(params.src_image, vk::ImageLayout::eTransferDstOptimal, - MakeClearColorValue(value.color), range); + MakeClearColorValue(clear.value.color), range); } else { cmdbuf.clearDepthStencilImage(params.src_image, vk::ImageLayout::eTransferDstOptimal, - MakeClearDepthStencilValue(value), range); + MakeClearDepthStencilValue(clear.value), range); } cmdbuf.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, params.pipeline_flags, @@ -381,13 +380,12 @@ bool TextureRuntime::ClearTexture(Surface& surface, const VideoCore::TextureClea return true; } - ClearTextureWithRenderpass(surface, clear, value); + ClearTextureWithRenderpass(surface, clear); return true; } void TextureRuntime::ClearTextureWithRenderpass(Surface& surface, - const VideoCore::TextureClear& clear, - VideoCore::ClearValue value) { + const VideoCore::TextureClear& clear) { const bool is_color = surface.type != VideoCore::SurfaceType::Depth && surface.type != VideoCore::SurfaceType::DepthStencil; @@ -450,7 +448,7 @@ void TextureRuntime::ClearTextureWithRenderpass(Surface& surface, }; renderpass_cache.EnterRenderpass(color_surface, depth_surface, render_area, true, - MakeClearValue(value)); + MakeClearValue(clear.value)); renderpass_cache.ExitRenderpass(); scheduler.Record([params, access_flag, pipeline_flags](vk::CommandBuffer cmdbuf) { diff --git a/src/video_core/renderer_vulkan/vk_texture_runtime.h b/src/video_core/renderer_vulkan/vk_texture_runtime.h index 5a60f50ff..5c74bf1e2 100644 --- a/src/video_core/renderer_vulkan/vk_texture_runtime.h +++ b/src/video_core/renderer_vulkan/vk_texture_runtime.h @@ -114,8 +114,7 @@ public: vk::ImageUsageFlags usage, vk::ImageAspectFlags aspect); /// Fills the rectangle of the texture with the clear value provided - bool ClearTexture(Surface& surface, const VideoCore::TextureClear& clear, - VideoCore::ClearValue value); + bool ClearTexture(Surface& surface, const VideoCore::TextureClear& clear); /// Copies a rectangle of src_tex to another rectange of dst_rect bool CopyTextures(Surface& source, Surface& dest, const VideoCore::TextureCopy& copy); @@ -140,8 +139,7 @@ public: private: /// Clears a partial texture rect using a clear rectangle - void ClearTextureWithRenderpass(Surface& surface, const VideoCore::TextureClear& clear, - VideoCore::ClearValue value); + void ClearTextureWithRenderpass(Surface& surface, const VideoCore::TextureClear& clear); /// Returns the current Vulkan instance const Instance& GetInstance() const {