Merge pull request #1281 from bunnei/multi-rt
gl_rasterizer: Implement multiple color attachments.
This commit is contained in:
commit
ac959799e4
|
@ -533,7 +533,11 @@ public:
|
||||||
u32 stencil_back_mask;
|
u32 stencil_back_mask;
|
||||||
u32 stencil_back_func_mask;
|
u32 stencil_back_func_mask;
|
||||||
|
|
||||||
INSERT_PADDING_WORDS(0x20);
|
INSERT_PADDING_WORDS(0x13);
|
||||||
|
|
||||||
|
u32 rt_separate_frag_data;
|
||||||
|
|
||||||
|
INSERT_PADDING_WORDS(0xC);
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
u32 address_high;
|
u32 address_high;
|
||||||
|
@ -557,7 +561,22 @@ public:
|
||||||
struct {
|
struct {
|
||||||
union {
|
union {
|
||||||
BitField<0, 4, u32> count;
|
BitField<0, 4, u32> count;
|
||||||
|
BitField<4, 3, u32> map_0;
|
||||||
|
BitField<7, 3, u32> map_1;
|
||||||
|
BitField<10, 3, u32> map_2;
|
||||||
|
BitField<13, 3, u32> map_3;
|
||||||
|
BitField<16, 3, u32> map_4;
|
||||||
|
BitField<19, 3, u32> map_5;
|
||||||
|
BitField<22, 3, u32> map_6;
|
||||||
|
BitField<25, 3, u32> map_7;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
u32 GetMap(size_t index) const {
|
||||||
|
const std::array<u32, NumRenderTargets> maps{map_0, map_1, map_2, map_3,
|
||||||
|
map_4, map_5, map_6, map_7};
|
||||||
|
ASSERT(index < maps.size());
|
||||||
|
return maps[index];
|
||||||
|
}
|
||||||
} rt_control;
|
} rt_control;
|
||||||
|
|
||||||
INSERT_PADDING_WORDS(0x2);
|
INSERT_PADDING_WORDS(0x2);
|
||||||
|
@ -968,6 +987,7 @@ ASSERT_REG_POSITION(clear_stencil, 0x368);
|
||||||
ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5);
|
ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5);
|
||||||
ASSERT_REG_POSITION(stencil_back_mask, 0x3D6);
|
ASSERT_REG_POSITION(stencil_back_mask, 0x3D6);
|
||||||
ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7);
|
ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7);
|
||||||
|
ASSERT_REG_POSITION(rt_separate_frag_data, 0x3EB);
|
||||||
ASSERT_REG_POSITION(zeta, 0x3F8);
|
ASSERT_REG_POSITION(zeta, 0x3F8);
|
||||||
ASSERT_REG_POSITION(vertex_attrib_format, 0x458);
|
ASSERT_REG_POSITION(vertex_attrib_format, 0x458);
|
||||||
ASSERT_REG_POSITION(rt_control, 0x487);
|
ASSERT_REG_POSITION(rt_control, 0x487);
|
||||||
|
|
|
@ -294,17 +294,10 @@ void RasterizerOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
|
||||||
cached_pages.add({pages_interval, delta});
|
cached_pages.add({pages_interval, delta});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb,
|
void RasterizerOpenGL::ConfigureFramebuffers(bool using_depth_fb, bool preserve_contents) {
|
||||||
bool using_depth_fb,
|
|
||||||
bool preserve_contents) {
|
|
||||||
MICROPROFILE_SCOPE(OpenGL_Framebuffer);
|
MICROPROFILE_SCOPE(OpenGL_Framebuffer);
|
||||||
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
||||||
|
|
||||||
if (regs.rt[0].format == Tegra::RenderTargetFormat::NONE) {
|
|
||||||
LOG_ERROR(HW_GPU, "RenderTargetFormat is not configured");
|
|
||||||
using_color_fb = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool has_stencil = regs.stencil_enable;
|
const bool has_stencil = regs.stencil_enable;
|
||||||
const bool write_color_fb =
|
const bool write_color_fb =
|
||||||
state.color_mask.red_enabled == GL_TRUE || state.color_mask.green_enabled == GL_TRUE ||
|
state.color_mask.red_enabled == GL_TRUE || state.color_mask.green_enabled == GL_TRUE ||
|
||||||
|
@ -314,41 +307,52 @@ std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_c
|
||||||
(state.depth.test_enabled && state.depth.write_mask == GL_TRUE) ||
|
(state.depth.test_enabled && state.depth.write_mask == GL_TRUE) ||
|
||||||
(has_stencil && (state.stencil.front.write_mask || state.stencil.back.write_mask));
|
(has_stencil && (state.stencil.front.write_mask || state.stencil.back.write_mask));
|
||||||
|
|
||||||
Surface color_surface;
|
|
||||||
Surface depth_surface;
|
Surface depth_surface;
|
||||||
MathUtil::Rectangle<u32> surfaces_rect;
|
if (using_depth_fb) {
|
||||||
std::tie(color_surface, depth_surface, surfaces_rect) =
|
depth_surface = res_cache.GetDepthBufferSurface(preserve_contents);
|
||||||
res_cache.GetFramebufferSurfaces(using_color_fb, using_depth_fb, preserve_contents);
|
}
|
||||||
|
|
||||||
const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()};
|
// TODO(bunnei): Figure out how the below register works. According to envytools, this should be
|
||||||
const MathUtil::Rectangle<u32> draw_rect{
|
// used to enable multiple render targets. However, it is left unset on all games that I have
|
||||||
static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.left,
|
// tested.
|
||||||
surfaces_rect.left, surfaces_rect.right)), // Left
|
ASSERT_MSG(regs.rt_separate_frag_data == 0, "Unimplemented");
|
||||||
static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.top,
|
|
||||||
surfaces_rect.bottom, surfaces_rect.top)), // Top
|
|
||||||
static_cast<u32>(std::clamp<s32>(static_cast<s32>(surfaces_rect.left) + viewport_rect.right,
|
|
||||||
surfaces_rect.left, surfaces_rect.right)), // Right
|
|
||||||
static_cast<u32>(
|
|
||||||
std::clamp<s32>(static_cast<s32>(surfaces_rect.bottom) + viewport_rect.bottom,
|
|
||||||
surfaces_rect.bottom, surfaces_rect.top))}; // Bottom
|
|
||||||
|
|
||||||
// Bind the framebuffer surfaces
|
// Bind the framebuffer surfaces
|
||||||
BindFramebufferSurfaces(color_surface, depth_surface, has_stencil);
|
state.draw.draw_framebuffer = framebuffer.handle;
|
||||||
|
|
||||||
SyncViewport(surfaces_rect);
|
|
||||||
|
|
||||||
// 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();
|
state.Apply();
|
||||||
|
|
||||||
// Only return the surface to be marked as dirty if writing to it is enabled.
|
std::array<GLenum, Maxwell::NumRenderTargets> buffers;
|
||||||
return std::make_pair(write_color_fb ? color_surface : nullptr,
|
for (size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
|
||||||
write_depth_fb ? depth_surface : nullptr);
|
Surface color_surface = res_cache.GetColorBufferSurface(index, preserve_contents);
|
||||||
|
buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index);
|
||||||
|
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,
|
||||||
|
GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index), GL_TEXTURE_2D,
|
||||||
|
color_surface != nullptr ? color_surface->Texture().handle : 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
glDrawBuffers(regs.rt_control.count, buffers.data());
|
||||||
|
|
||||||
|
if (depth_surface) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
SyncViewport();
|
||||||
|
|
||||||
|
state.Apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerOpenGL::Clear() {
|
void RasterizerOpenGL::Clear() {
|
||||||
|
@ -407,8 +411,7 @@ void RasterizerOpenGL::Clear() {
|
||||||
|
|
||||||
ScopeAcquireGLContext acquire_context{emu_window};
|
ScopeAcquireGLContext acquire_context{emu_window};
|
||||||
|
|
||||||
auto [dirty_color_surface, dirty_depth_surface] =
|
ConfigureFramebuffers(use_depth_fb, false);
|
||||||
ConfigureFramebuffers(use_color_fb, use_depth_fb, false);
|
|
||||||
|
|
||||||
clear_state.Apply();
|
clear_state.Apply();
|
||||||
|
|
||||||
|
@ -430,8 +433,7 @@ void RasterizerOpenGL::DrawArrays() {
|
||||||
|
|
||||||
ScopeAcquireGLContext acquire_context{emu_window};
|
ScopeAcquireGLContext acquire_context{emu_window};
|
||||||
|
|
||||||
const auto [dirty_color_surface, dirty_depth_surface] =
|
ConfigureFramebuffers(true, true);
|
||||||
ConfigureFramebuffers(true, regs.zeta.Address() != 0 && regs.zeta_enable != 0, true);
|
|
||||||
|
|
||||||
SyncDepthTestState();
|
SyncDepthTestState();
|
||||||
SyncStencilTestState();
|
SyncStencilTestState();
|
||||||
|
@ -729,38 +731,12 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
|
||||||
return current_unit + static_cast<u32>(entries.size());
|
return current_unit + static_cast<u32>(entries.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface,
|
void RasterizerOpenGL::SyncViewport() {
|
||||||
const Surface& depth_surface, bool has_stencil) {
|
|
||||||
state.draw.draw_framebuffer = framebuffer.handle;
|
|
||||||
state.Apply();
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RasterizerOpenGL::SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect) {
|
|
||||||
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
||||||
const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()};
|
const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()};
|
||||||
|
|
||||||
state.viewport.x = static_cast<GLint>(surfaces_rect.left) + viewport_rect.left;
|
state.viewport.x = viewport_rect.left;
|
||||||
state.viewport.y = static_cast<GLint>(surfaces_rect.bottom) + viewport_rect.bottom;
|
state.viewport.y = viewport_rect.bottom;
|
||||||
state.viewport.width = static_cast<GLsizei>(viewport_rect.GetWidth());
|
state.viewport.width = static_cast<GLsizei>(viewport_rect.GetWidth());
|
||||||
state.viewport.height = static_cast<GLsizei>(viewport_rect.GetHeight());
|
state.viewport.height = static_cast<GLsizei>(viewport_rect.GetHeight());
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,14 +97,8 @@ private:
|
||||||
GLvec4 border_color;
|
GLvec4 border_color;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Configures the color and depth framebuffer states and returns the dirty <Color, Depth>
|
/// Configures the color and depth framebuffer states
|
||||||
/// surfaces if writing was enabled.
|
void ConfigureFramebuffers(bool using_depth_fb, bool preserve_contents);
|
||||||
std::pair<Surface, Surface> ConfigureFramebuffers(bool using_color_fb, bool using_depth_fb,
|
|
||||||
bool preserve_contents);
|
|
||||||
|
|
||||||
/// Binds the framebuffer color and depth surface
|
|
||||||
void BindFramebufferSurfaces(const Surface& color_surface, const Surface& depth_surface,
|
|
||||||
bool has_stencil);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Configures the current constbuffers to use for the draw command.
|
* Configures the current constbuffers to use for the draw command.
|
||||||
|
@ -127,7 +121,7 @@ private:
|
||||||
u32 current_unit);
|
u32 current_unit);
|
||||||
|
|
||||||
/// Syncs the viewport to match the guest state
|
/// Syncs the viewport to match the guest state
|
||||||
void SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect);
|
void SyncViewport();
|
||||||
|
|
||||||
/// Syncs the clip enabled status to match the guest state
|
/// Syncs the clip enabled status to match the guest state
|
||||||
void SyncClipEnabled();
|
void SyncClipEnabled();
|
||||||
|
|
|
@ -61,8 +61,8 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*static*/ SurfaceParams SurfaceParams::CreateForFramebuffer(
|
/*static*/ SurfaceParams SurfaceParams::CreateForFramebuffer(size_t index) {
|
||||||
const Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig& config) {
|
const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]};
|
||||||
SurfaceParams params{};
|
SurfaceParams params{};
|
||||||
params.addr = TryGetCpuAddr(config.Address());
|
params.addr = TryGetCpuAddr(config.Address());
|
||||||
params.is_tiled = true;
|
params.is_tiled = true;
|
||||||
|
@ -708,62 +708,34 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextu
|
||||||
return GetSurface(SurfaceParams::CreateForTexture(config));
|
return GetSurface(SurfaceParams::CreateForTexture(config));
|
||||||
}
|
}
|
||||||
|
|
||||||
SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(bool using_color_fb,
|
Surface RasterizerCacheOpenGL::GetDepthBufferSurface(bool preserve_contents) {
|
||||||
bool using_depth_fb,
|
const auto& regs{Core::System::GetInstance().GPU().Maxwell3D().regs};
|
||||||
bool preserve_contents) {
|
if (!regs.zeta.Address() || !regs.zeta_enable) {
|
||||||
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
|
return {};
|
||||||
|
|
||||||
// TODO(bunnei): This is hard corded to use just the first render buffer
|
|
||||||
LOG_TRACE(Render_OpenGL, "hard-coded for render target 0!");
|
|
||||||
|
|
||||||
// get color and depth surfaces
|
|
||||||
SurfaceParams color_params{};
|
|
||||||
SurfaceParams depth_params{};
|
|
||||||
|
|
||||||
if (using_color_fb) {
|
|
||||||
color_params = SurfaceParams::CreateForFramebuffer(regs.rt[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (using_depth_fb) {
|
SurfaceParams depth_params{SurfaceParams::CreateForDepthBuffer(
|
||||||
depth_params = SurfaceParams::CreateForDepthBuffer(regs.zeta_width, regs.zeta_height,
|
regs.zeta_width, regs.zeta_height, regs.zeta.Address(), regs.zeta.format)};
|
||||||
regs.zeta.Address(), regs.zeta.format);
|
|
||||||
|
return GetSurface(depth_params, preserve_contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
Surface RasterizerCacheOpenGL::GetColorBufferSurface(size_t index, bool preserve_contents) {
|
||||||
|
const auto& regs{Core::System::GetInstance().GPU().Maxwell3D().regs};
|
||||||
|
|
||||||
|
ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets);
|
||||||
|
|
||||||
|
if (index >= regs.rt_control.count) {
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
MathUtil::Rectangle<u32> color_rect{};
|
if (regs.rt[index].Address() == 0 || regs.rt[index].format == Tegra::RenderTargetFormat::NONE) {
|
||||||
Surface color_surface;
|
return {};
|
||||||
if (using_color_fb) {
|
|
||||||
color_surface = GetSurface(color_params, preserve_contents);
|
|
||||||
if (color_surface) {
|
|
||||||
color_rect = color_surface->GetSurfaceParams().GetRect();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MathUtil::Rectangle<u32> depth_rect{};
|
const SurfaceParams color_params{SurfaceParams::CreateForFramebuffer(index)};
|
||||||
Surface depth_surface;
|
|
||||||
if (using_depth_fb) {
|
|
||||||
depth_surface = GetSurface(depth_params, preserve_contents);
|
|
||||||
if (depth_surface) {
|
|
||||||
depth_rect = depth_surface->GetSurfaceParams().GetRect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MathUtil::Rectangle<u32> fb_rect{};
|
return GetSurface(color_params, preserve_contents);
|
||||||
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 ||
|
|
||||||
color_rect.left != depth_rect.left || color_rect.right != depth_rect.right) {
|
|
||||||
color_surface = GetSurface(color_params);
|
|
||||||
depth_surface = GetSurface(depth_params);
|
|
||||||
fb_rect = color_surface->GetSurfaceParams().GetRect();
|
|
||||||
}
|
|
||||||
} else if (color_surface) {
|
|
||||||
fb_rect = color_rect;
|
|
||||||
} else if (depth_surface) {
|
|
||||||
fb_rect = depth_rect;
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::make_tuple(color_surface, depth_surface, fb_rect);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) {
|
void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) {
|
||||||
|
|
|
@ -669,8 +669,7 @@ struct SurfaceParams {
|
||||||
static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config);
|
static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config);
|
||||||
|
|
||||||
/// Creates SurfaceParams from a framebuffer configuration
|
/// Creates SurfaceParams from a framebuffer configuration
|
||||||
static SurfaceParams CreateForFramebuffer(
|
static SurfaceParams CreateForFramebuffer(size_t index);
|
||||||
const Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig& config);
|
|
||||||
|
|
||||||
/// Creates SurfaceParams for a depth buffer configuration
|
/// Creates SurfaceParams for a depth buffer configuration
|
||||||
static SurfaceParams CreateForDepthBuffer(u32 zeta_width, u32 zeta_height,
|
static SurfaceParams CreateForDepthBuffer(u32 zeta_width, u32 zeta_height,
|
||||||
|
@ -774,9 +773,11 @@ public:
|
||||||
/// Get a surface based on the texture configuration
|
/// Get a surface based on the texture configuration
|
||||||
Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config);
|
Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config);
|
||||||
|
|
||||||
/// Get the color and depth surfaces based on the framebuffer configuration
|
/// Get the depth surface based on the framebuffer configuration
|
||||||
SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb,
|
Surface GetDepthBufferSurface(bool preserve_contents);
|
||||||
bool preserve_contents);
|
|
||||||
|
/// Get the color surface based on the framebuffer configuration and the specified render target
|
||||||
|
Surface GetColorBufferSurface(size_t index, bool preserve_contents);
|
||||||
|
|
||||||
/// Flushes the surface to Switch memory
|
/// Flushes the surface to Switch memory
|
||||||
void FlushSurface(const Surface& surface);
|
void FlushSurface(const Surface& surface);
|
||||||
|
|
Reference in New Issue