rasterizer_cache: Wrap common draw operation in FramebufferBase

* Makes the rasterizer draw method much cleaner
This commit is contained in:
GPUCode
2023-02-14 13:29:20 +02:00
parent ee0a7476b3
commit d42ccb387a
19 changed files with 539 additions and 353 deletions

View File

@ -28,7 +28,8 @@ add_library(video_core STATIC
regs_texturing.h
renderer_base.cpp
renderer_base.h
rasterizer_cache/texture_codec.h
rasterizer_cache/framebuffer_base.cpp
rasterizer_cache/framebuffer_base.h
rasterizer_cache/pixel_format.cpp
rasterizer_cache/pixel_format.h
rasterizer_cache/rasterizer_cache.cpp
@ -36,8 +37,9 @@ add_library(video_core STATIC
rasterizer_cache/rasterizer_cache_base.h
rasterizer_cache/sampler_params.h
rasterizer_cache/slot_vector.h
rasterizer_cache/surface_base.h
rasterizer_cache/surface_base.cpp
rasterizer_cache/surface_base.h
rasterizer_cache/texture_codec.h
rasterizer_cache/utils.cpp
rasterizer_cache/utils.h
rasterizer_cache/surface_params.cpp

View File

@ -0,0 +1,64 @@
// Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "video_core/rasterizer_cache/framebuffer_base.h"
#include "video_core/rasterizer_cache/surface_base.h"
#include "video_core/regs.h"
namespace VideoCore {
FramebufferBase::FramebufferBase() = default;
FramebufferBase::FramebufferBase(const Pica::Regs& regs, SurfaceBase* const color_,
SurfaceBase* const depth_stencil_,
Common::Rectangle<u32> surfaces_rect)
: color{color_}, depth_stencil{depth_stencil_} {
res_scale = color ? color->res_scale : (depth_stencil ? depth_stencil->res_scale : 1u);
// Determine the draw rectangle (render area + scissor)
const Common::Rectangle viewport_rect = regs.rasterizer.GetViewportRect();
draw_rect.left =
std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.left * res_scale,
surfaces_rect.left, surfaces_rect.right);
draw_rect.top =
std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.top * res_scale,
surfaces_rect.bottom, surfaces_rect.top);
draw_rect.right =
std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.right * res_scale,
surfaces_rect.left, surfaces_rect.right);
draw_rect.bottom =
std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.bottom * res_scale,
surfaces_rect.bottom, surfaces_rect.top);
// Update viewport
viewport.x = surfaces_rect.left + viewport_rect.left * res_scale;
viewport.y = surfaces_rect.bottom + viewport_rect.bottom * res_scale;
viewport.width = viewport_rect.GetWidth() * res_scale;
viewport.height = viewport_rect.GetHeight() * res_scale;
// 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.
scissor_rect.left =
static_cast<s32>(surfaces_rect.left + regs.rasterizer.scissor_test.x1 * res_scale);
scissor_rect.bottom =
static_cast<s32>(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.
scissor_rect.right =
static_cast<s32>(surfaces_rect.left + (regs.rasterizer.scissor_test.x2 + 1) * res_scale);
scissor_rect.top =
static_cast<s32>(surfaces_rect.bottom + (regs.rasterizer.scissor_test.y2 + 1) * res_scale);
// Query surface invalidation intervals
const Common::Rectangle draw_rect_unscaled{draw_rect / res_scale};
if (color) {
intervals[0] = color->GetSubRectInterval(draw_rect_unscaled);
}
if (depth_stencil) {
intervals[1] = depth_stencil->GetSubRectInterval(draw_rect_unscaled);
}
}
} // namespace VideoCore

View File

@ -0,0 +1,86 @@
// Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/math_util.h"
#include "video_core/rasterizer_cache/utils.h"
namespace Pica {
struct Regs;
}
namespace VideoCore {
class SurfaceBase;
struct ViewportInfo {
f32 x;
f32 y;
f32 width;
f32 height;
};
/**
* A framebuffer is a lightweight abstraction over a pair of surfaces and provides
* metadata about them.
*/
class FramebufferBase {
public:
FramebufferBase();
FramebufferBase(const Pica::Regs& regs, SurfaceBase* const color,
SurfaceBase* const depth_stencil, Common::Rectangle<u32> surfaces_rect);
SurfaceBase* Color() const noexcept {
return color;
}
SurfaceBase* DepthStencil() const noexcept {
return depth_stencil;
}
SurfaceInterval Interval(SurfaceType type) const noexcept {
return intervals[Index(type)];
}
u32 ResolutionScale() const noexcept {
return res_scale;
}
Common::Rectangle<u32> DrawRect() const noexcept {
return draw_rect;
}
Common::Rectangle<s32> Scissor() const noexcept {
return scissor_rect;
}
ViewportInfo Viewport() const noexcept {
return viewport;
}
protected:
u32 Index(VideoCore::SurfaceType type) const noexcept {
switch (type) {
case VideoCore::SurfaceType::Color:
return 0;
case VideoCore::SurfaceType::DepthStencil:
return 1;
default:
LOG_CRITICAL(Render_Vulkan, "Unknown surface type in framebuffer");
return 0;
}
}
protected:
SurfaceBase* color{};
SurfaceBase* depth_stencil{};
std::array<SurfaceInterval, 2> intervals{};
Common::Rectangle<s32> scissor_rect{};
Common::Rectangle<u32> draw_rect{};
ViewportInfo viewport;
u32 res_scale{1};
};
} // namespace VideoCore

View File

@ -707,32 +707,28 @@ auto RasterizerCache<T>::GetTextureCube(const TextureCubeConfig& config) -> cons
}
template <class T>
auto RasterizerCache<T>::GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb,
const Common::Rectangle<s32>& viewport_rect)
-> SurfaceSurfaceRect_Tuple {
auto RasterizerCache<T>::GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb)
-> Framebuffer {
const auto& regs = Pica::g_state.regs;
const auto& config = regs.framebuffer.framebuffer;
// Update resolution_scale_factor and reset cache if changed
const bool resolution_scale_changed =
resolution_scale_factor != VideoCore::GetResolutionScaleFactor();
const bool texture_filter_changed =
/*VideoCore::g_texture_filter_update_requested.exchange(false) &&
texture_filterer->Reset(Settings::values.texture_filter_name,
VideoCore::GetResolutionScaleFactor())*/
false;
if (resolution_scale_changed || texture_filter_changed) [[unlikely]] {
if (resolution_scale_changed) [[unlikely]] {
resolution_scale_factor = VideoCore::GetResolutionScaleFactor();
UnregisterAll();
}
Common::Rectangle<u32> viewport_clamped{
static_cast<u32>(std::clamp(viewport_rect.left, 0, static_cast<s32>(config.GetWidth()))),
static_cast<u32>(std::clamp(viewport_rect.top, 0, static_cast<s32>(config.GetHeight()))),
static_cast<u32>(std::clamp(viewport_rect.right, 0, static_cast<s32>(config.GetWidth()))),
static_cast<u32>(
std::clamp(viewport_rect.bottom, 0, static_cast<s32>(config.GetHeight())))};
const s32 framebuffer_width = config.GetWidth();
const s32 framebuffer_height = config.GetHeight();
const auto viewport_rect = regs.rasterizer.GetViewportRect();
const Common::Rectangle<u32> viewport_clamped = {
static_cast<u32>(std::clamp(viewport_rect.left, 0, framebuffer_width)),
static_cast<u32>(std::clamp(viewport_rect.top, 0, framebuffer_height)),
static_cast<u32>(std::clamp(viewport_rect.right, 0, framebuffer_width)),
static_cast<u32>(std::clamp(viewport_rect.bottom, 0, framebuffer_height)),
};
// get color and depth surfaces
SurfaceParams color_params;
@ -756,25 +752,25 @@ auto RasterizerCache<T>::GetFramebufferSurfaces(bool using_color_fb, bool using_
// Make sure 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!");
LOG_CRITICAL(HW_GPU, "Color and depth framebuffer memory regions overlap; "
"overlapping framebuffers not supported!");
using_depth_fb = false;
}
Common::Rectangle<u32> color_rect{};
Surface color_surface = nullptr;
Surface color_surface{};
if (using_color_fb)
std::tie(color_surface, color_rect) =
GetSurfaceSubRect(color_params, ScaleMatch::Exact, false);
Common::Rectangle<u32> depth_rect{};
Surface depth_surface = nullptr;
Surface depth_surface{};
if (using_depth_fb)
std::tie(depth_surface, depth_rect) =
GetSurfaceSubRect(depth_params, ScaleMatch::Exact, false);
Common::Rectangle<u32> fb_rect{};
if (color_surface != nullptr && depth_surface != nullptr) {
if (color_surface && depth_surface) {
fb_rect = color_rect;
// Color and Depth surfaces must have the same dimensions and offsets
if (color_rect.bottom != depth_rect.bottom || color_rect.top != depth_rect.top ||
@ -783,24 +779,46 @@ auto RasterizerCache<T>::GetFramebufferSurfaces(bool using_color_fb, bool using_
depth_surface = GetSurface(depth_params, ScaleMatch::Exact, false);
fb_rect = color_surface->GetScaledRect();
}
} else if (color_surface != nullptr) {
} else if (color_surface) {
fb_rect = color_rect;
} else if (depth_surface != nullptr) {
} else if (depth_surface) {
fb_rect = depth_rect;
}
if (color_surface != nullptr) {
if (color_surface) {
ValidateSurface(color_surface, boost::icl::first(color_vp_interval),
boost::icl::length(color_vp_interval));
color_surface->InvalidateAllWatcher();
}
if (depth_surface != nullptr) {
if (depth_surface) {
ValidateSurface(depth_surface, boost::icl::first(depth_vp_interval),
boost::icl::length(depth_vp_interval));
depth_surface->InvalidateAllWatcher();
}
return std::make_tuple(color_surface, depth_surface, fb_rect);
render_targets = RenderTargets{
.color_surface = color_surface,
.depth_surface = depth_surface,
};
auto* const color = color_surface ? color_surface.get() : nullptr;
auto* const depth_stencil = depth_surface ? depth_surface.get() : nullptr;
return Framebuffer{runtime, color, depth_stencil, regs, fb_rect};
}
template <class T>
void RasterizerCache<T>::InvalidateRenderTargets(const Framebuffer& framebuffer) {
const auto Invalidate = [&](SurfaceType type, Surface region_owner) {
const bool has_attachment = framebuffer.HasAttachment(type);
if (has_attachment) {
const SurfaceInterval interval = framebuffer.Interval(type);
InvalidateRegion(boost::icl::first(interval), boost::icl::length(interval),
region_owner);
}
};
Invalidate(SurfaceType::Color, render_targets.color_surface);
Invalidate(SurfaceType::DepthStencil, render_targets.depth_surface);
}
template <class T>

View File

@ -45,8 +45,9 @@ class RasterizerCache : NonCopyable {
static constexpr u64 CITRA_PAGEBITS = 18;
using TextureRuntime = typename T::RuntimeType;
using Sampler = typename T::Sampler;
using Surface = std::shared_ptr<typename T::SurfaceType>;
using Sampler = typename T::Sampler;
using Framebuffer = typename T::Framebuffer;
/// Declare rasterizer interval types
using SurfaceMap = boost::icl::interval_map<PAddr, Surface, boost::icl::partial_absorber,
@ -54,7 +55,6 @@ class RasterizerCache : NonCopyable {
boost::icl::inter_section, SurfaceInterval>;
using SurfaceRect_Tuple = std::tuple<Surface, Common::Rectangle<u32>>;
using SurfaceSurfaceRect_Tuple = std::tuple<Surface, Surface, Common::Rectangle<u32>>;
using PageMap = boost::icl::interval_map<u32, int>;
public:
@ -95,8 +95,10 @@ public:
const Surface& GetTextureCube(const TextureCubeConfig& config);
/// Get the color and depth surfaces based on the framebuffer configuration
SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb,
const Common::Rectangle<s32>& viewport_rect);
Framebuffer GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb);
/// Marks the draw rectangle defined in framebuffer as invalid
void InvalidateRenderTargets(const Framebuffer& framebuffer);
/// Get a surface that matches a "texture copy" display transfer config
SurfaceRect_Tuple GetTexCopySurface(const SurfaceParams& params);
@ -196,6 +198,13 @@ private:
std::unordered_map<SamplerParams, SamplerId> samplers;
SlotVector<Sampler> slot_samplers;
struct RenderTargets {
Surface color_surface;
Surface depth_surface;
};
RenderTargets render_targets;
};
} // namespace VideoCore

View File

@ -5,6 +5,7 @@
#pragma once
#include <memory>
#include <boost/icl/interval_set.hpp>
#include "video_core/rasterizer_cache/surface_params.h"
namespace VideoCore {

View File

@ -4,13 +4,10 @@
#pragma once
#include <boost/icl/interval_set.hpp>
#include "video_core/rasterizer_cache/utils.h"
namespace VideoCore {
using SurfaceInterval = boost::icl::right_open_interval<PAddr>;
class SurfaceParams {
public:
/// Returns true if other_surface matches exactly params

View File

@ -5,6 +5,7 @@
#pragma once
#include <span>
#include <boost/icl/right_open_interval.hpp>
#include "common/hash.h"
#include "common/math_util.h"
#include "common/vector_math.h"
@ -20,6 +21,8 @@ using SamplerId = SlotId;
/// Fake sampler ID for null samplers
constexpr SamplerId NULL_SAMPLER_ID{0};
using SurfaceInterval = boost::icl::right_open_interval<PAddr>;
struct Offset {
constexpr auto operator<=>(const Offset&) const noexcept = default;

View File

@ -18,6 +18,8 @@
namespace OpenGL {
using VideoCore::SurfaceType;
constexpr std::size_t VERTEX_BUFFER_SIZE = 16 * 1024 * 1024;
constexpr std::size_t INDEX_BUFFER_SIZE = 1 * 1024 * 1024;
constexpr std::size_t UNIFORM_BUFFER_SIZE = 2 * 1024 * 1024;
@ -355,95 +357,36 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
(write_depth_fb || regs.framebuffer.output_merger.depth_test_enable != 0 ||
(has_stencil && state.stencil.test_enabled));
const Common::Rectangle viewport_rect_unscaled = regs.rasterizer.GetViewportRect();
const auto [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);
const Common::Rectangle draw_rect{
static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.left) +
viewport_rect_unscaled.left * res_scale,
surfaces_rect.left, surfaces_rect.right)), // Left
static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) +
viewport_rect_unscaled.top * res_scale,
surfaces_rect.bottom, surfaces_rect.top)), // Top
static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.left) +
viewport_rect_unscaled.right * res_scale,
surfaces_rect.left, surfaces_rect.right)), // Right
static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) +
viewport_rect_unscaled.bottom * res_scale,
surfaces_rect.bottom, surfaces_rect.top))}; // Bottom
// Bind the framebuffer surfaces
state.draw.draw_framebuffer = framebuffer.handle;
state.Apply();
if (shadow_rendering) {
if (color_surface == nullptr) {
return true;
}
glFramebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH,
color_surface->width * color_surface->res_scale);
glFramebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT,
color_surface->height * color_surface->res_scale);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
0);
state.image_shadow_buffer = color_surface->texture.handle;
} else {
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
color_surface != nullptr ? color_surface->texture.handle : 0, 0);
if (depth_surface != nullptr) {
if (has_stencil) {
// 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);
}
const Framebuffer framebuffer =
res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb);
const bool has_color = framebuffer.HasAttachment(SurfaceType::Color);
const bool has_depth_stencil = framebuffer.HasAttachment(SurfaceType::DepthStencil);
if (!has_color && (shadow_rendering || !has_depth_stencil)) {
return true;
}
// Sync the viewport
state.viewport.x =
static_cast<GLint>(surfaces_rect.left) + viewport_rect_unscaled.left * res_scale;
state.viewport.y =
static_cast<GLint>(surfaces_rect.bottom) + viewport_rect_unscaled.bottom * res_scale;
state.viewport.width = static_cast<GLsizei>(viewport_rect_unscaled.GetWidth() * res_scale);
state.viewport.height = static_cast<GLsizei>(viewport_rect_unscaled.GetHeight() * res_scale);
// Bind the framebuffer surfaces
if (shadow_rendering) {
state.image_shadow_buffer = framebuffer.Attachment(SurfaceType::Color);
}
state.draw.draw_framebuffer = framebuffer.Handle();
state.Apply();
// Sync the viewport
const auto viewport = framebuffer.Viewport();
state.viewport.x = viewport.x;
state.viewport.y = viewport.y;
state.viewport.width = viewport.width;
state.viewport.height = viewport.height;
const u32 res_scale = framebuffer.ResolutionScale();
if (uniform_block_data.data.framebuffer_scale != res_scale) {
uniform_block_data.data.framebuffer_scale = res_scale;
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>(surfaces_rect.left + regs.rasterizer.scissor_test.x1 * res_scale);
GLint scissor_y1 =
static_cast<GLint>(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<GLint>(surfaces_rect.left + (regs.rasterizer.scissor_test.x2 + 1) * res_scale);
GLint scissor_y2 = static_cast<GLint>(surfaces_rect.bottom +
(regs.rasterizer.scissor_test.y2 + 1) * res_scale);
// Update scissor uniforms
const auto [scissor_x1, scissor_y2, scissor_x2, scissor_y1] = framebuffer.Scissor();
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 ||
@ -456,20 +399,13 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
uniform_block_data.dirty = true;
}
bool need_duplicate_texture = false;
auto CheckBarrier = [&need_duplicate_texture, &color_surface = color_surface](GLuint handle) {
if (color_surface && color_surface->texture.handle == handle) {
need_duplicate_texture = true;
}
};
const auto BindCubeFace = [&](GLuint& target, Pica::TexturingRegs::CubeFace face,
Pica::Texture::TextureInfo& info) {
info.physical_address = regs.texturing.GetCubePhysicalAddress(face);
auto surface = res_cache.GetTextureSurface(info);
if (surface != nullptr) {
CheckBarrier(target = surface->texture.handle);
target = surface->texture.handle;
} else {
target = 0;
}
@ -488,7 +424,7 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
case TextureType::Shadow2D: {
const auto surface = res_cache.GetTextureSurface(texture);
if (surface) {
CheckBarrier(state.image_shadow_texture_px = surface->texture.handle);
state.image_shadow_texture_px = surface->texture.handle;
} else {
state.image_shadow_texture_px = 0;
}
@ -536,8 +472,7 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
auto surface = res_cache.GetTextureSurface(texture);
if (surface != nullptr) {
CheckBarrier(state.texture_units[texture_index].texture_2d =
surface->texture.handle);
state.texture_units[texture_index].texture_2d = surface->texture.handle;
} else {
// Can occur when texture addr is null or its memory is unmapped/invalid
// HACK: In this case, the correct behaviour for the PICA is to use the last
@ -553,36 +488,6 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
}
}
// The game is trying to use a surface as a texture and framebuffer at the same time
// which causes unpredictable behavior on the host.
// Making a copy to sample from eliminates this issue and seems to be fairly cheap.
if (need_duplicate_texture) {
Surface temp{*color_surface, runtime};
const VideoCore::TextureCopy copy = {
.src_level = 0,
.dst_level = 0,
.src_layer = 0,
.dst_layer = 0,
.src_offset = {0, 0},
.dst_offset = {0, 0},
.extent = {temp.GetScaledWidth(), temp.GetScaledHeight()},
};
runtime.CopyTextures(*color_surface, temp, copy);
for (auto& unit : state.texture_units) {
if (unit.texture_2d == color_surface->texture.handle) {
unit.texture_2d = temp.Handle();
}
}
for (auto shadow_unit : {&state.image_shadow_texture_nx, &state.image_shadow_texture_ny,
&state.image_shadow_texture_nz, &state.image_shadow_texture_px,
&state.image_shadow_texture_py, &state.image_shadow_texture_pz}) {
if (*shadow_unit == color_surface->texture.handle) {
*shadow_unit = temp.Handle();
}
}
}
// Sync and bind the shader
if (shader_dirty) {
SetShader();
@ -596,10 +501,9 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
// Sync the uniform data
UploadUniforms(accelerate);
// Viewport can have negative offsets or larger
// dimensions than our framebuffer sub-rect.
// Enable scissor test to prevent drawing
// outside of the framebuffer region
// Viewport can have negative offsets or larger dimensions than our framebuffer sub-rect.
// Enable scissor test to prevent drawing outside of the framebuffer region
const auto draw_rect = framebuffer.DrawRect();
state.scissor.enabled = true;
state.scissor.x = draw_rect.left;
state.scissor.y = draw_rect.bottom;
@ -636,37 +540,13 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
vertex_batch.clear();
// Reset textures in rasterizer state context because the rasterizer cache might delete them
for (u32 texture_index = 0; texture_index < pica_textures.size(); ++texture_index) {
state.texture_units[texture_index].texture_2d = 0;
}
state.texture_cube_unit.texture_cube = 0;
state.image_shadow_texture_px = 0;
state.image_shadow_texture_nx = 0;
state.image_shadow_texture_py = 0;
state.image_shadow_texture_ny = 0;
state.image_shadow_texture_pz = 0;
state.image_shadow_texture_nz = 0;
state.image_shadow_buffer = 0;
state.Apply();
if (shadow_rendering) {
glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT |
GL_TEXTURE_UPDATE_BARRIER_BIT | GL_FRAMEBUFFER_BARRIER_BIT);
}
// Mark framebuffer surfaces as dirty
const Common::Rectangle draw_rect_unscaled{draw_rect / res_scale};
if (color_surface && 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 && write_depth_fb) {
auto interval = depth_surface->GetSubRectInterval(draw_rect_unscaled);
res_cache.InvalidateRegion(boost::icl::first(interval), boost::icl::length(interval),
depth_surface);
}
res_cache.InvalidateRenderTargets(framebuffer);
return succeeded;
}

View File

@ -6,6 +6,7 @@
#include "common/scope_exit.h"
#include "common/settings.h"
#include "video_core/rasterizer_cache/utils.h"
#include "video_core/regs.h"
#include "video_core/renderer_opengl/gl_driver.h"
#include "video_core/renderer_opengl/gl_format_reinterpreter.h"
#include "video_core/renderer_opengl/gl_state.h"
@ -473,6 +474,74 @@ void Surface::ScaledDownload(const VideoCore::BufferTextureCopy& download,
}
}
Framebuffer::Framebuffer(TextureRuntime& runtime, Surface* const color,
Surface* const depth_stencil, const Pica::Regs& regs,
Common::Rectangle<u32> surfaces_rect)
: VideoCore::FramebufferBase{regs, color, depth_stencil, surfaces_rect} {
const bool shadow_rendering = regs.framebuffer.IsShadowRendering();
const bool has_stencil = regs.framebuffer.HasStencil();
if (shadow_rendering && !color) {
return; // Framebuffer won't get used
}
if (color) {
attachments[0] = color->Handle();
}
if (depth_stencil) {
attachments[1] = depth_stencil->Handle();
}
const u64 hash = Common::ComputeStructHash64(attachments);
auto [it, new_framebuffer] = runtime.framebuffer_cache.try_emplace(hash);
if (!new_framebuffer) {
handle = it->second.handle;
return;
}
// Create a new framebuffer otherwise
OGLFramebuffer& framebuffer = it->second;
framebuffer.Create();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle);
if (shadow_rendering) {
glFramebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH,
color->width * res_scale);
glFramebufferParameteri(GL_DRAW_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT,
color->height * res_scale);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
0);
} else {
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
color ? color->Handle() : 0, 0);
if (depth_stencil) {
if (has_stencil) {
// Attach both depth and stencil
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
GL_TEXTURE_2D, depth_stencil->Handle(), 0);
} else {
// Attach depth
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
depth_stencil->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);
}
}
// Restore previous framebuffer
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, OpenGLState::GetCurState().draw.draw_framebuffer);
}
Framebuffer::~Framebuffer() = default;
Sampler::Sampler(TextureRuntime& runtime, VideoCore::SamplerParams params) {
const GLenum mag_filter = PicaToGL::TextureMagFilterMode(params.min_filter);
const GLenum min_filter = PicaToGL::TextureMinFilterMode(params.min_filter, params.mip_filter);

View File

@ -6,6 +6,7 @@
#include <set>
#include <span>
#include "video_core/rasterizer_cache/framebuffer_base.h"
#include "video_core/rasterizer_cache/rasterizer_cache_base.h"
#include "video_core/rasterizer_cache/surface_base.h"
#include "video_core/renderer_opengl/gl_format_reinterpreter.h"
@ -36,6 +37,7 @@ class Surface;
*/
class TextureRuntime {
friend class Surface;
friend class Framebuffer;
public:
TextureRuntime(Driver& driver);
@ -94,6 +96,7 @@ private:
TextureFilterer filterer;
std::array<ReinterpreterList, VideoCore::PIXEL_FORMAT_COUNT> reinterpreters;
std::unordered_multimap<VideoCore::HostTextureTag, OGLTexture> texture_recycler;
std::unordered_map<u64, OGLFramebuffer, Common::IdentityHash<u64>> framebuffer_cache;
StreamBuffer upload_buffer;
std::vector<u8> download_buffer;
OGLFramebuffer read_fbo, draw_fbo;
@ -135,6 +138,30 @@ public:
OGLTexture texture{};
};
class Framebuffer : public VideoCore::FramebufferBase {
public:
explicit Framebuffer(TextureRuntime& runtime, Surface* const color,
Surface* const depth_stencil, const Pica::Regs& regs,
Common::Rectangle<u32> surfaces_rect);
~Framebuffer();
[[nodiscard]] GLuint Handle() const noexcept {
return handle;
}
[[nodiscard]] GLuint Attachment(VideoCore::SurfaceType type) const noexcept {
return attachments[Index(type)];
}
bool HasAttachment(VideoCore::SurfaceType type) const noexcept {
return static_cast<bool>(attachments[Index(type)]);
}
private:
std::array<GLuint, 2> attachments{};
GLuint handle{};
};
class Sampler {
public:
explicit Sampler(TextureRuntime& runtime, VideoCore::SamplerParams params);
@ -158,6 +185,7 @@ struct Traits {
using RuntimeType = TextureRuntime;
using SurfaceType = Surface;
using Sampler = Sampler;
using Framebuffer = Framebuffer;
};
using RasterizerCache = VideoCore::RasterizerCache<Traits>;

View File

@ -680,16 +680,6 @@ void PipelineCache::BindSampler(u32 binding, vk::Sampler sampler) {
desc_manager.SetBinding(2, binding, data);
}
void PipelineCache::SetViewport(float x, float y, float width, float height) {
const vk::Viewport viewport{x, y, width, height, 0.f, 1.f};
scheduler.Record([viewport](vk::CommandBuffer cmdbuf) { cmdbuf.setViewport(0, viewport); });
}
void PipelineCache::SetScissor(s32 x, s32 y, u32 width, u32 height) {
const vk::Rect2D scissor{{x, y}, {width, height}};
scheduler.Record([scissor](vk::CommandBuffer cmdbuf) { cmdbuf.setScissor(0, scissor); });
}
void PipelineCache::ApplyDynamic(const PipelineInfo& info, bool is_dirty) {
scheduler.Record([is_dirty, current_dynamic = current_info.dynamic,
dynamic = info.dynamic](vk::CommandBuffer cmdbuf) {

View File

@ -218,12 +218,6 @@ public:
/// Binds a sampler to the specified binding
void BindSampler(u32 binding, vk::Sampler sampler);
/// Sets the viewport rectangle to the provided values
void SetViewport(float x, float y, float width, float height);
/// Sets the scissor rectange to the provided values
void SetScissor(s32 x, s32 y, u32 width, u32 height);
private:
/// Applies dynamic pipeline state to the current command buffer
void ApplyDynamic(const PipelineInfo& info, bool is_dirty);

View File

@ -25,6 +25,7 @@ MICROPROFILE_DEFINE(Vulkan_Blits, "Vulkan", "Blits", MP_RGB(100, 100, 255));
namespace Vulkan {
using TriangleTopology = Pica::PipelineRegs::TriangleTopology;
using VideoCore::SurfaceType;
constexpr u64 STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
constexpr u64 TEXTURE_BUFFER_SIZE = 2 * 1024 * 1024;
@ -437,7 +438,6 @@ void RasterizerVulkan::DrawTriangles() {
return;
}
const u64 vertex_size = vertex_batch.size() * sizeof(HardwareVertex);
pipeline_info.rasterization.topology.Assign(Pica::PipelineRegs::TriangleTopology::List);
pipeline_info.vertex_layout = software_layout;
@ -462,57 +462,25 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
(write_depth_fb || regs.framebuffer.output_merger.depth_test_enable != 0 ||
(has_stencil && pipeline_info.depth_stencil.stencil_test_enable));
const Common::Rectangle viewport_rect_unscaled = regs.rasterizer.GetViewportRect();
const auto [color_surface, depth_surface, surfaces_rect] =
res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb, viewport_rect_unscaled);
if (!color_surface && (shadow_rendering || !depth_surface)) {
const Framebuffer framebuffer =
res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb);
const bool has_color = framebuffer.HasAttachment(SurfaceType::Color);
const bool has_depth_stencil = framebuffer.HasAttachment(SurfaceType::DepthStencil);
if (!has_color && (shadow_rendering || !has_depth_stencil)) {
return true;
}
pipeline_info.attachments.color_format =
color_surface ? color_surface->pixel_format : VideoCore::PixelFormat::Invalid;
pipeline_info.attachments.depth_format =
depth_surface ? depth_surface->pixel_format : VideoCore::PixelFormat::Invalid;
const u16 res_scale = color_surface != nullptr
? color_surface->res_scale
: (depth_surface == nullptr ? 1u : depth_surface->res_scale);
const VideoCore::Rect2D draw_rect = {
static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.left) +
viewport_rect_unscaled.left * res_scale,
surfaces_rect.left, surfaces_rect.right)), // Left
static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) +
viewport_rect_unscaled.top * res_scale,
surfaces_rect.bottom, surfaces_rect.top)), // Top
static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.left) +
viewport_rect_unscaled.right * res_scale,
surfaces_rect.left, surfaces_rect.right)), // Right
static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) +
viewport_rect_unscaled.bottom * res_scale,
surfaces_rect.bottom, surfaces_rect.top))};
pipeline_info.attachments.color_format = framebuffer.Format(SurfaceType::Color);
pipeline_info.attachments.depth_format = framebuffer.Format(SurfaceType::DepthStencil);
const u32 res_scale = framebuffer.ResolutionScale();
if (uniform_block_data.data.framebuffer_scale != res_scale) {
uniform_block_data.data.framebuffer_scale = res_scale;
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.
int scissor_x1 =
static_cast<int>(surfaces_rect.left + regs.rasterizer.scissor_test.x1 * res_scale);
int scissor_y1 =
static_cast<int>(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.
int scissor_x2 =
static_cast<int>(surfaces_rect.left + (regs.rasterizer.scissor_test.x2 + 1) * res_scale);
int scissor_y2 =
static_cast<int>(surfaces_rect.bottom + (regs.rasterizer.scissor_test.y2 + 1) * res_scale);
// Update scissor uniforms
const auto [scissor_x1, scissor_y2, scissor_x2, scissor_y1] = framebuffer.Scissor();
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 ||
@ -527,14 +495,8 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
// Sync and bind the texture surfaces
// NOTE: From here onwards its a safe zone to set the draw state, doing that any earlier will
// cause issues as the rasterizer cache might cause a scheduler switch and invalidate our state
SyncTextureUnits(color_surface.get());
const vk::Rect2D render_area = {
.offset{static_cast<s32>(draw_rect.left), static_cast<s32>(draw_rect.bottom)},
.extent{draw_rect.GetWidth(), draw_rect.GetHeight()},
};
renderpass_cache.EnterRenderpass(color_surface.get(), depth_surface.get(), render_area);
// cause issues as the rasterizer cache might cause a scheduler flush and invalidate our state
SyncTextureUnits(framebuffer);
// Sync and bind the shader
if (shader_dirty) {
@ -549,16 +511,26 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
// Sync the uniform data
UploadUniforms(accelerate);
// Sync the viewport
pipeline_cache.SetViewport(surfaces_rect.left + viewport_rect_unscaled.left * res_scale,
surfaces_rect.bottom + viewport_rect_unscaled.bottom * res_scale,
viewport_rect_unscaled.GetWidth() * res_scale,
viewport_rect_unscaled.GetHeight() * res_scale);
// Begin the renderpass
renderpass_cache.EnterRenderpass(framebuffer);
// Sync the viewport
// Viewport can have negative offsets or larger dimensions than our framebuffer sub-rect.
// Enable scissor test to prevent drawing outside of the framebuffer region
pipeline_cache.SetScissor(draw_rect.left, draw_rect.bottom, draw_rect.GetWidth(),
draw_rect.GetHeight());
scheduler.Record([viewport = framebuffer.Viewport(),
scissor = framebuffer.RenderArea()](vk::CommandBuffer cmdbuf) {
const vk::Viewport vk_viewport = {
.x = viewport.x,
.y = viewport.y,
.width = viewport.width,
.height = viewport.height,
.minDepth = 0.f,
.maxDepth = 1.f,
};
cmdbuf.setViewport(0, vk_viewport);
cmdbuf.setScissor(0, scissor);
});
// Draw the vertex batch
bool succeeded = true;
@ -583,18 +555,7 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
vertex_batch.clear();
// Mark framebuffer surfaces as dirty
const Common::Rectangle draw_rect_unscaled{draw_rect / res_scale};
if (color_surface && 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 && write_depth_fb) {
auto interval = depth_surface->GetSubRectInterval(draw_rect_unscaled);
res_cache.InvalidateRegion(boost::icl::first(interval), boost::icl::length(interval),
depth_surface);
}
res_cache.InvalidateRenderTargets(framebuffer);
static int counter = 20;
counter--;
@ -606,8 +567,10 @@ bool RasterizerVulkan::Draw(bool accelerate, bool is_indexed) {
return succeeded;
}
void RasterizerVulkan::SyncTextureUnits(Surface* const color_surface) {
void RasterizerVulkan::SyncTextureUnits(const Framebuffer& framebuffer) {
const auto pica_textures = regs.texturing.GetTextures();
const vk::ImageView color_view = framebuffer.ImageView(SurfaceType::Color);
for (u32 texture_index = 0; texture_index < pica_textures.size(); ++texture_index) {
const auto& texture = pica_textures[texture_index];
@ -676,8 +639,8 @@ void RasterizerVulkan::SyncTextureUnits(Surface* const color_surface) {
auto surface = res_cache.GetTextureSurface(texture);
if (surface) {
if (color_surface && color_surface->ImageView() == surface->ImageView()) {
Surface temp{*color_surface, runtime};
if (color_view == surface->ImageView()) {
Surface temp{*framebuffer.Color(), runtime};
const VideoCore::TextureCopy copy = {
.src_level = 0,
.dst_level = 0,
@ -687,7 +650,7 @@ void RasterizerVulkan::SyncTextureUnits(Surface* const color_surface) {
.dst_offset = {0, 0},
.extent = {temp.GetScaledWidth(), temp.GetScaledHeight()},
};
runtime.CopyTextures(*color_surface, temp, copy);
runtime.CopyTextures(static_cast<Surface&>(*framebuffer.Color()), temp, copy);
pipeline_cache.BindTexture(texture_index, temp.ImageView());
} else {
pipeline_cache.BindTexture(texture_index, surface->ImageView());

View File

@ -93,7 +93,7 @@ private:
void SyncAndUploadLUTsLF();
/// Syncs all enabled PICA texture units
void SyncTextureUnits(Surface* const color_surface);
void SyncTextureUnits(const Framebuffer& framebuffer);
/// Upload the uniform blocks to the uniform buffer object
void UploadUniforms(bool accelerate_draw);

View File

@ -11,6 +11,9 @@
namespace Vulkan {
using VideoCore::PixelFormat;
using VideoCore::SurfaceType;
RenderpassCache::RenderpassCache(const Instance& instance, Scheduler& scheduler)
: instance{instance}, scheduler{scheduler}, dynamic_rendering{
instance.IsDynamicRenderingSupported()} {}
@ -38,66 +41,50 @@ RenderpassCache::~RenderpassCache() {
void RenderpassCache::EnterRenderpass(Surface* const color, Surface* const depth_stencil,
vk::Rect2D render_area, bool do_clear, vk::ClearValue clear) {
ASSERT(color || depth_stencil);
return EnterRenderpass(Framebuffer{color, depth_stencil, render_area}, do_clear, clear);
}
void RenderpassCache::EnterRenderpass(const Framebuffer& framebuffer, bool do_clear,
vk::ClearValue clear) {
if (dynamic_rendering) {
return BeginRendering(color, depth_stencil, render_area, do_clear, clear);
return BeginRendering(framebuffer, do_clear, clear);
}
u32 width = UINT32_MAX;
u32 height = UINT32_MAX;
u32 cursor = 0;
std::array<VideoCore::PixelFormat, 2> formats{};
std::array<vk::Image, 2> images{};
std::array<vk::ImageView, 2> views{};
const auto Prepare = [&](Surface* const surface) {
if (!surface) {
formats[cursor++] = VideoCore::PixelFormat::Invalid;
return;
}
width = std::min(width, surface->GetScaledWidth());
height = std::min(height, surface->GetScaledHeight());
formats[cursor] = surface->pixel_format;
images[cursor] = surface->Image();
views[cursor++] = surface->FramebufferView();
};
Prepare(color);
Prepare(depth_stencil);
const RenderingInfo new_info = {
.color =
RenderTarget{
.aspect = vk::ImageAspectFlagBits::eColor,
.image = images[0],
.image_view = views[0],
},
.depth =
RenderTarget{
.aspect = depth_stencil ? depth_stencil->Aspect() : vk::ImageAspectFlagBits::eDepth,
.image = images[1],
.image_view = views[1],
},
.render_area = render_area,
RenderingInfo new_info = {
.color{
.aspect = vk::ImageAspectFlagBits::eColor,
.image = framebuffer.Image(SurfaceType::Color),
.image_view = framebuffer.ImageView(SurfaceType::Color),
},
.depth{
.aspect = vk::ImageAspectFlagBits::eDepth,
.image = framebuffer.Image(SurfaceType::DepthStencil),
.image_view = framebuffer.ImageView(SurfaceType::Color),
},
.render_area = framebuffer.RenderArea(),
.clear = clear,
.do_clear = do_clear,
};
const PixelFormat color_format = framebuffer.Format(SurfaceType::Color);
const PixelFormat depth_format = framebuffer.Format(SurfaceType::DepthStencil);
if (depth_format == PixelFormat::D24S8) {
new_info.depth.aspect |= vk::ImageAspectFlagBits::eStencil;
}
const bool is_dirty = scheduler.IsStateDirty(StateFlags::Renderpass);
if (info == new_info && rendering && !is_dirty) {
cmd_count++;
return;
}
const vk::RenderPass renderpass = GetRenderpass(formats[0], formats[1], do_clear);
const vk::RenderPass renderpass = GetRenderpass(color_format, depth_format, do_clear);
const FramebufferInfo framebuffer_info = {
.color = views[0],
.depth = views[1],
.width = width,
.height = height,
.color = new_info.color.image_view,
.depth = new_info.depth.image_view,
.width = framebuffer.Width(),
.height = framebuffer.Height(),
};
auto [it, new_framebuffer] = framebuffers.try_emplace(framebuffer_info);
@ -108,18 +95,18 @@ void RenderpassCache::EnterRenderpass(Surface* const color, Surface* const depth
if (rendering) {
ExitRenderpass();
}
scheduler.Record(
[render_area, clear, renderpass, framebuffer = it->second](vk::CommandBuffer cmdbuf) {
const vk::RenderPassBeginInfo renderpass_begin_info = {
.renderPass = renderpass,
.framebuffer = framebuffer,
.renderArea = render_area,
.clearValueCount = 1,
.pClearValues = &clear,
};
scheduler.Record([render_area = new_info.render_area, clear, renderpass,
framebuffer = it->second](vk::CommandBuffer cmdbuf) {
const vk::RenderPassBeginInfo renderpass_begin_info = {
.renderPass = renderpass,
.framebuffer = framebuffer,
.renderArea = render_area,
.clearValueCount = 1,
.pClearValues = &clear,
};
cmdbuf.beginRenderPass(renderpass_begin_info, vk::SubpassContents::eInline);
});
cmdbuf.beginRenderPass(renderpass_begin_info, vk::SubpassContents::eInline);
});
scheduler.MarkStateNonDirty(StateFlags::Renderpass);
info = new_info;
@ -200,27 +187,27 @@ void RenderpassCache::ExitRenderpass() {
}
}
void RenderpassCache::BeginRendering(Surface* const color, Surface* const depth_stencil,
vk::Rect2D render_area, bool do_clear, vk::ClearValue clear) {
void RenderpassCache::BeginRendering(const Framebuffer& framebuffer, bool do_clear,
vk::ClearValue clear) {
RenderingInfo new_info = {
.render_area = render_area,
.color{
.aspect = vk::ImageAspectFlagBits::eColor,
.image = framebuffer.Image(SurfaceType::Color),
.image_view = framebuffer.ImageView(SurfaceType::Color),
},
.depth{
.aspect = vk::ImageAspectFlagBits::eDepth,
.image = framebuffer.Image(SurfaceType::DepthStencil),
.image_view = framebuffer.ImageView(SurfaceType::DepthStencil),
},
.render_area = framebuffer.RenderArea(),
.clear = clear,
.do_clear = do_clear,
};
if (color) {
new_info.color = RenderTarget{
.aspect = vk::ImageAspectFlagBits::eColor,
.image = color->Image(),
.image_view = color->FramebufferView(),
};
}
if (depth_stencil) {
new_info.depth = RenderTarget{
.aspect = depth_stencil->Aspect(),
.image = depth_stencil->Image(),
.image_view = depth_stencil->FramebufferView(),
};
const bool has_stencil = framebuffer.Format(SurfaceType::DepthStencil) == PixelFormat::D24S8;
if (has_stencil) {
new_info.depth.aspect |= vk::ImageAspectFlagBits::eStencil;
}
const bool is_dirty = scheduler.IsStateDirty(StateFlags::Renderpass);
@ -229,9 +216,6 @@ void RenderpassCache::BeginRendering(Surface* const color, Surface* const depth_
return;
}
const bool has_stencil =
depth_stencil && depth_stencil->type == VideoCore::SurfaceType::DepthStencil;
if (rendering) {
ExitRenderpass();
}

View File

@ -39,6 +39,8 @@ struct hash<Vulkan::FramebufferInfo> {
namespace Vulkan {
class Framebuffer;
class RenderpassCache {
static constexpr std::size_t MAX_COLOR_FORMATS = 5;
static constexpr std::size_t MAX_DEPTH_FORMATS = 4;
@ -48,6 +50,8 @@ public:
~RenderpassCache();
/// Begins a new renderpass only when no other renderpass is currently active
void EnterRenderpass(const Framebuffer& framebuffer, bool do_clear = false,
vk::ClearValue clear = {});
void EnterRenderpass(Surface* const color, Surface* const depth_stencil, vk::Rect2D render_area,
bool do_clear = false, vk::ClearValue clear = {});
@ -68,8 +72,7 @@ public:
private:
/// Begins a new rendering scope using dynamic rendering
void BeginRendering(Surface* const color, Surface* const depth_stencil, vk::Rect2D render_area,
bool do_clear, vk::ClearValue clear);
void BeginRendering(const Framebuffer& framebuffer, bool do_clear, vk::ClearValue clear);
/// Creates a renderpass configured appropriately and stores it in cached_renderpasses
vk::RenderPass CreateRenderPass(vk::Format color, vk::Format depth,

View File

@ -1227,6 +1227,50 @@ void Surface::DepthStencilDownload(const VideoCore::BufferTextureCopy& download,
r32_surface.Download(r32_download, staging);
}
Framebuffer::Framebuffer(Surface* const color, Surface* const depth_stencil,
vk::Rect2D render_area_)
: render_area{render_area_} {
PrepareImages(color, depth_stencil);
}
Framebuffer::Framebuffer(TextureRuntime& runtime, Surface* const color,
Surface* const depth_stencil, const Pica::Regs& regs,
Common::Rectangle<u32> surfaces_rect)
: VideoCore::FramebufferBase{regs, color, depth_stencil, surfaces_rect} {
// Update render area
render_area.offset.x = draw_rect.left;
render_area.offset.y = draw_rect.bottom;
render_area.extent.width = draw_rect.GetWidth();
render_area.extent.height = draw_rect.GetHeight();
PrepareImages(color, depth_stencil);
}
Framebuffer::~Framebuffer() = default;
void Framebuffer::PrepareImages(Surface* const color, Surface* const depth_stencil) {
u32 cursor{0};
width = height = std::numeric_limits<u32>::max();
const auto Prepare = [&](Surface* const surface) {
if (!surface) {
formats[cursor++] = VideoCore::PixelFormat::Invalid;
return;
}
width = std::min(width, surface->GetScaledWidth());
height = std::min(height, surface->GetScaledHeight());
formats[cursor] = surface->pixel_format;
images[cursor] = surface->Image();
image_views[cursor++] = surface->ImageView();
};
// Setup image handles
Prepare(color);
Prepare(depth_stencil);
}
Sampler::Sampler(TextureRuntime& runtime, VideoCore::SamplerParams params)
: device{runtime.GetInstance().GetDevice()} {
using TextureConfig = VideoCore::SamplerParams::TextureConfig;

View File

@ -7,6 +7,7 @@
#include <set>
#include <span>
#include <vulkan/vulkan_hash.hpp>
#include "video_core/rasterizer_cache/framebuffer_base.h"
#include "video_core/rasterizer_cache/rasterizer_cache_base.h"
#include "video_core/rasterizer_cache/surface_base.h"
#include "video_core/renderer_vulkan/vk_blit_helper.h"
@ -235,6 +236,55 @@ private:
bool is_storage{};
};
class Framebuffer : public VideoCore::FramebufferBase {
public:
explicit Framebuffer(Surface* const color, Surface* const depth_stencil,
vk::Rect2D render_area);
explicit Framebuffer(TextureRuntime& runtime, Surface* const color,
Surface* const depth_stencil, const Pica::Regs& regs,
Common::Rectangle<u32> surfaces_rect);
~Framebuffer();
VideoCore::PixelFormat Format(VideoCore::SurfaceType type) const noexcept {
return formats[Index(type)];
}
[[nodiscard]] vk::Image Image(VideoCore::SurfaceType type) const noexcept {
return images[Index(type)];
}
[[nodiscard]] vk::ImageView ImageView(VideoCore::SurfaceType type) const noexcept {
return image_views[Index(type)];
}
bool HasAttachment(VideoCore::SurfaceType type) const noexcept {
return static_cast<bool>(image_views[Index(type)]);
}
u32 Width() const noexcept {
return width;
}
u32 Height() const noexcept {
return height;
}
vk::Rect2D RenderArea() const noexcept {
return render_area;
}
private:
void PrepareImages(Surface* const color, Surface* const depth_stencil);
private:
std::array<vk::Image, 2> images{};
std::array<vk::ImageView, 2> image_views{};
std::array<VideoCore::PixelFormat, 2> formats{};
vk::Rect2D render_area{};
u32 width{};
u32 height{};
};
/**
* @brief A sampler is used to configure the sampling parameters of a texture unit
*/
@ -269,6 +319,7 @@ struct Traits {
using RuntimeType = TextureRuntime;
using SurfaceType = Surface;
using Sampler = Sampler;
using Framebuffer = Framebuffer;
};
using RasterizerCache = VideoCore::RasterizerCache<Traits>;