aboutsummaryrefslogtreecommitdiff
path: root/src/video_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core')
-rw-r--r--src/video_core/engines/maxwell_3d.h20
-rw-r--r--src/video_core/engines/maxwell_compute.cpp19
-rw-r--r--src/video_core/engines/maxwell_compute.h36
-rw-r--r--src/video_core/engines/shader_bytecode.h27
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp46
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h11
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp436
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h64
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h3
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp32
-rw-r--r--src/video_core/renderer_opengl/gl_state.h9
-rw-r--r--src/video_core/renderer_opengl/gl_stream_buffer.cpp2
-rw-r--r--src/video_core/textures/decoders.cpp117
-rw-r--r--src/video_core/textures/texture.h2
-rw-r--r--src/video_core/utils.h22
17 files changed, 643 insertions, 213 deletions
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index b81b0723d..9f5581045 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -41,6 +41,7 @@ public:
static constexpr std::size_t NumCBData = 16;
static constexpr std::size_t NumVertexArrays = 32;
static constexpr std::size_t NumVertexAttributes = 32;
+ static constexpr std::size_t NumTextureSamplers = 32;
static constexpr std::size_t MaxShaderProgram = 6;
static constexpr std::size_t MaxShaderStage = 5;
// Maximum number of const buffers per shader stage.
@@ -461,7 +462,11 @@ public:
u32 entry;
} macros;
- INSERT_PADDING_WORDS(0x1B8);
+ INSERT_PADDING_WORDS(0x189);
+
+ u32 tfb_enabled;
+
+ INSERT_PADDING_WORDS(0x2E);
RenderTargetConfig rt[NumRenderTargets];
@@ -594,7 +599,9 @@ public:
u32 depth_write_enabled;
- INSERT_PADDING_WORDS(0x7);
+ u32 alpha_test_enabled;
+
+ INSERT_PADDING_WORDS(0x6);
u32 d3d_cull_mode;
@@ -635,7 +642,11 @@ public:
u32 vb_element_base;
- INSERT_PADDING_WORDS(0x40);
+ INSERT_PADDING_WORDS(0x38);
+
+ float point_size;
+
+ INSERT_PADDING_WORDS(0x7);
u32 zeta_enable;
@@ -977,6 +988,7 @@ private:
"Field " #field_name " has invalid position")
ASSERT_REG_POSITION(macros, 0x45);
+ASSERT_REG_POSITION(tfb_enabled, 0x1D1);
ASSERT_REG_POSITION(rt, 0x200);
ASSERT_REG_POSITION(viewport_transform[0], 0x280);
ASSERT_REG_POSITION(viewport, 0x300);
@@ -996,6 +1008,7 @@ ASSERT_REG_POSITION(zeta_height, 0x48b);
ASSERT_REG_POSITION(depth_test_enable, 0x4B3);
ASSERT_REG_POSITION(independent_blend_enable, 0x4B9);
ASSERT_REG_POSITION(depth_write_enabled, 0x4BA);
+ASSERT_REG_POSITION(alpha_test_enabled, 0x4BB);
ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2);
ASSERT_REG_POSITION(depth_test_func, 0x4C3);
ASSERT_REG_POSITION(blend, 0x4CF);
@@ -1009,6 +1022,7 @@ ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6);
ASSERT_REG_POSITION(stencil_front_mask, 0x4E7);
ASSERT_REG_POSITION(screen_y_control, 0x4EB);
ASSERT_REG_POSITION(vb_element_base, 0x50D);
+ASSERT_REG_POSITION(point_size, 0x546);
ASSERT_REG_POSITION(zeta_enable, 0x54E);
ASSERT_REG_POSITION(tsc, 0x557);
ASSERT_REG_POSITION(tic, 0x55D);
diff --git a/src/video_core/engines/maxwell_compute.cpp b/src/video_core/engines/maxwell_compute.cpp
index e4e5f9e5e..59e28b22d 100644
--- a/src/video_core/engines/maxwell_compute.cpp
+++ b/src/video_core/engines/maxwell_compute.cpp
@@ -2,12 +2,29 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/logging/log.h"
+#include "core/core.h"
#include "video_core/engines/maxwell_compute.h"
namespace Tegra {
namespace Engines {
-void MaxwellCompute::WriteReg(u32 method, u32 value) {}
+void MaxwellCompute::WriteReg(u32 method, u32 value) {
+ ASSERT_MSG(method < Regs::NUM_REGS,
+ "Invalid MaxwellCompute register, increase the size of the Regs structure");
+
+ regs.reg_array[method] = value;
+
+ switch (method) {
+ case MAXWELL_COMPUTE_REG_INDEX(compute): {
+ LOG_CRITICAL(HW_GPU, "Compute shaders are not implemented");
+ UNREACHABLE();
+ break;
+ }
+ default:
+ break;
+ }
+}
} // namespace Engines
} // namespace Tegra
diff --git a/src/video_core/engines/maxwell_compute.h b/src/video_core/engines/maxwell_compute.h
index 2b3e4ced6..6ea934fb9 100644
--- a/src/video_core/engines/maxwell_compute.h
+++ b/src/video_core/engines/maxwell_compute.h
@@ -4,17 +4,53 @@
#pragma once
+#include <array>
+#include "common/assert.h"
+#include "common/bit_field.h"
+#include "common/common_funcs.h"
#include "common/common_types.h"
namespace Tegra::Engines {
+#define MAXWELL_COMPUTE_REG_INDEX(field_name) \
+ (offsetof(Tegra::Engines::MaxwellCompute::Regs, field_name) / sizeof(u32))
+
class MaxwellCompute final {
public:
MaxwellCompute() = default;
~MaxwellCompute() = default;
+ struct Regs {
+ static constexpr std::size_t NUM_REGS = 0xCF8;
+
+ union {
+ struct {
+ INSERT_PADDING_WORDS(0x281);
+
+ union {
+ u32 compute_end;
+ BitField<0, 1, u32> unknown;
+ } compute;
+
+ INSERT_PADDING_WORDS(0xA76);
+ };
+ std::array<u32, NUM_REGS> reg_array;
+ };
+ } regs{};
+
+ static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32),
+ "MaxwellCompute Regs has wrong size");
+
/// Write the value to the register identified by method.
void WriteReg(u32 method, u32 value);
};
+#define ASSERT_REG_POSITION(field_name, position) \
+ static_assert(offsetof(MaxwellCompute::Regs, field_name) == position * 4, \
+ "Field " #field_name " has invalid position")
+
+ASSERT_REG_POSITION(compute, 0x281);
+
+#undef ASSERT_REG_POSITION
+
} // namespace Tegra::Engines
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 7e1de0fa1..b1f137b9c 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -5,9 +5,8 @@
#pragma once
#include <bitset>
-#include <cstring>
-#include <map>
#include <string>
+#include <tuple>
#include <vector>
#include <boost/optional.hpp>
@@ -315,17 +314,29 @@ enum class TextureMiscMode : u64 {
PTP,
};
-enum class IpaInterpMode : u64 { Linear = 0, Perspective = 1, Flat = 2, Sc = 3 };
-enum class IpaSampleMode : u64 { Default = 0, Centroid = 1, Offset = 2 };
+enum class IpaInterpMode : u64 {
+ Linear = 0,
+ Perspective = 1,
+ Flat = 2,
+ Sc = 3,
+};
+
+enum class IpaSampleMode : u64 {
+ Default = 0,
+ Centroid = 1,
+ Offset = 2,
+};
struct IpaMode {
IpaInterpMode interpolation_mode;
IpaSampleMode sampling_mode;
- inline bool operator==(const IpaMode& a) {
- return (a.interpolation_mode == interpolation_mode) && (a.sampling_mode == sampling_mode);
+
+ bool operator==(const IpaMode& a) const {
+ return std::tie(interpolation_mode, sampling_mode) ==
+ std::tie(a.interpolation_mode, a.sampling_mode);
}
- inline bool operator!=(const IpaMode& a) {
- return !((*this) == a);
+ bool operator!=(const IpaMode& a) const {
+ return !operator==(a);
}
};
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 274c2dbcf..14d82a7bc 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -383,7 +383,7 @@ void RasterizerOpenGL::Clear() {
bool use_stencil{};
OpenGLState clear_state;
- clear_state.draw.draw_framebuffer = state.draw.draw_framebuffer;
+ clear_state.draw.draw_framebuffer = framebuffer.handle;
clear_state.color_mask.red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE;
clear_state.color_mask.green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE;
clear_state.color_mask.blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE;
@@ -450,6 +450,9 @@ void RasterizerOpenGL::DrawArrays() {
SyncBlendState();
SyncLogicOpState();
SyncCullMode();
+ SyncAlphaTest();
+ SyncTransformFeedback();
+ SyncPointState();
// TODO(bunnei): Sync framebuffer_scale uniform here
// TODO(bunnei): Sync scissorbox uniform(s) here
@@ -484,8 +487,13 @@ void RasterizerOpenGL::DrawArrays() {
GLintptr index_buffer_offset = 0;
if (is_indexed) {
MICROPROFILE_SCOPE(OpenGL_Index);
- index_buffer_offset =
- buffer_cache.UploadMemory(regs.index_array.StartAddress(), index_buffer_size);
+
+ // Adjust the index buffer offset so it points to the first desired index.
+ auto index_start = regs.index_array.StartAddress();
+ index_start += static_cast<size_t>(regs.index_array.first) *
+ static_cast<size_t>(regs.index_array.FormatSizeInBytes());
+
+ index_buffer_offset = buffer_cache.UploadMemory(index_start, index_buffer_size);
}
SetupShaders();
@@ -499,10 +507,6 @@ void RasterizerOpenGL::DrawArrays() {
if (is_indexed) {
const GLint base_vertex{static_cast<GLint>(regs.vb_element_base)};
- // Adjust the index buffer offset so it points to the first desired index.
- index_buffer_offset += static_cast<GLintptr>(regs.index_array.first) *
- static_cast<GLintptr>(regs.index_array.FormatSizeInBytes());
-
if (gpu.state.current_instance > 0) {
glDrawElementsInstancedBaseVertexBaseInstance(
primitive_mode, regs.index_array.count,
@@ -734,7 +738,7 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
}
texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc);
- Surface surface = res_cache.GetTextureSurface(texture);
+ Surface surface = res_cache.GetTextureSurface(texture, entry);
if (surface != nullptr) {
state.texture_units[current_bindpoint].texture = surface->Texture().handle;
state.texture_units[current_bindpoint].target = surface->Target();
@@ -882,4 +886,30 @@ void RasterizerOpenGL::SyncLogicOpState() {
state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation);
}
+void RasterizerOpenGL::SyncAlphaTest() {
+ const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
+
+ // TODO(Rodrigo): Alpha testing is a legacy OpenGL feature, but it can be
+ // implemented with a test+discard in fragment shaders.
+ if (regs.alpha_test_enabled != 0) {
+ LOG_CRITICAL(Render_OpenGL, "Alpha testing is not implemented");
+ UNREACHABLE();
+ }
+}
+
+void RasterizerOpenGL::SyncTransformFeedback() {
+ const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
+
+ if (regs.tfb_enabled != 0) {
+ LOG_CRITICAL(Render_OpenGL, "Transform feedbacks are not implemented");
+ UNREACHABLE();
+ }
+}
+
+void RasterizerOpenGL::SyncPointState() {
+ const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
+
+ state.point.size = regs.point_size;
+}
+
} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index bf9560bdc..4c8ecbd1c 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -158,6 +158,15 @@ private:
/// Syncs the LogicOp state to match the guest state
void SyncLogicOpState();
+ /// Syncs the alpha test state to match the guest state
+ void SyncAlphaTest();
+
+ /// Syncs the transform feedback state to match the guest state
+ void SyncTransformFeedback();
+
+ /// Syncs the point state to match the guest state
+ void SyncPointState();
+
bool has_ARB_direct_state_access = false;
bool has_ARB_multi_bind = false;
bool has_ARB_separate_shader_objects = false;
@@ -178,7 +187,7 @@ private:
OGLVertexArray>
vertex_array_cache;
- std::array<SamplerInfo, GLShader::NumTextureSamplers> texture_samplers;
+ std::array<SamplerInfo, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> texture_samplers;
static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
OGLBufferCache buffer_cache;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 86682d7cb..ce967c4d6 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -41,7 +41,7 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
}
/*static*/ SurfaceParams SurfaceParams::CreateForTexture(
- const Tegra::Texture::FullTextureInfo& config) {
+ const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) {
SurfaceParams params{};
params.addr = TryGetCpuAddr(config.tic.Address());
params.is_tiled = config.tic.IsTiled();
@@ -60,9 +60,23 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
case SurfaceTarget::Texture2D:
params.depth = 1;
break;
+ case SurfaceTarget::TextureCubemap:
+ params.depth = config.tic.Depth() * 6;
+ break;
case SurfaceTarget::Texture3D:
+ params.depth = config.tic.Depth();
+ break;
case SurfaceTarget::Texture2DArray:
params.depth = config.tic.Depth();
+ if (!entry.IsArray()) {
+ // TODO(bunnei): We have seen games re-use a Texture2D as Texture2DArray with depth of
+ // one, but sample the texture in the shader as if it were not an array texture. This
+ // probably is valid on hardware, but we still need to write a test to confirm this. In
+ // emulation, the workaround here is to continue to treat this as a Texture2D. An
+ // example game that does this is Super Mario Odyssey (in Cloud Kingdom).
+ ASSERT(params.depth == 1);
+ params.target = SurfaceTarget::Texture2D;
+ }
break;
default:
LOG_CRITICAL(HW_GPU, "Unknown depth for target={}", static_cast<u32>(params.target));
@@ -71,7 +85,11 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
break;
}
- params.size_in_bytes = params.SizeInBytes();
+ params.size_in_bytes_total = params.SizeInBytesTotal();
+ params.size_in_bytes_2d = params.SizeInBytes2D();
+ params.max_mip_level = config.tic.max_mip_level + 1;
+ params.rt = {};
+
return params;
}
@@ -89,7 +107,16 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
params.unaligned_height = config.height;
params.target = SurfaceTarget::Texture2D;
params.depth = 1;
- params.size_in_bytes = params.SizeInBytes();
+ params.size_in_bytes_total = params.SizeInBytesTotal();
+ params.size_in_bytes_2d = params.SizeInBytes2D();
+ params.max_mip_level = 0;
+
+ // Render target specific parameters, not used for caching
+ params.rt.index = static_cast<u32>(index);
+ params.rt.array_mode = config.array_mode;
+ params.rt.layer_stride = config.layer_stride;
+ params.rt.base_layer = config.base_layer;
+
return params;
}
@@ -108,7 +135,11 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
params.unaligned_height = zeta_height;
params.target = SurfaceTarget::Texture2D;
params.depth = 1;
- params.size_in_bytes = params.SizeInBytes();
+ params.size_in_bytes_total = params.SizeInBytesTotal();
+ params.size_in_bytes_2d = params.SizeInBytes2D();
+ params.max_mip_level = 0;
+ params.rt = {};
+
return params;
}
@@ -141,8 +172,8 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
{GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
true}, // BC7U
{GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8,
- ComponentType::UNorm, true}, // BC6H_UF16
- {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
+ ComponentType::Float, true}, // BC6H_UF16
+ {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::Float,
true}, // BC6H_SF16
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4
{GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // G8R8U
@@ -400,9 +431,13 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr),
// clang-format on
};
-static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect, GLuint dst_tex,
- const MathUtil::Rectangle<u32>& dst_rect, SurfaceType type,
- GLuint read_fb_handle, GLuint draw_fb_handle) {
+static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
+ GLuint read_fb_handle, GLuint draw_fb_handle, GLenum src_attachment = 0,
+ GLenum dst_attachment = 0, std::size_t cubemap_face = 0) {
+
+ const auto& src_params{src_surface->GetSurfaceParams()};
+ const auto& dst_params{dst_surface->GetSurfaceParams()};
+
OpenGLState prev_state{OpenGLState::GetCurState()};
SCOPE_EXIT({ prev_state.Apply(); });
@@ -413,47 +448,203 @@ static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rec
u32 buffers{};
- if (type == SurfaceType::ColorTexture) {
- glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, src_tex,
- 0);
- glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
- 0);
+ if (src_params.type == SurfaceType::ColorTexture) {
+ switch (src_params.target) {
+ case SurfaceParams::SurfaceTarget::Texture2D:
+ glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
+ GL_TEXTURE_2D, src_surface->Texture().handle, 0);
+ glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
+ 0, 0);
+ break;
+ case SurfaceParams::SurfaceTarget::TextureCubemap:
+ glFramebufferTexture2D(
+ GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
+ static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face),
+ src_surface->Texture().handle, 0);
+ glFramebufferTexture2D(
+ GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
+ static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), 0, 0);
+ break;
+ case SurfaceParams::SurfaceTarget::Texture2DArray:
+ glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
+ src_surface->Texture().handle, 0, 0);
+ glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 0, 0, 0);
+ break;
+ case SurfaceParams::SurfaceTarget::Texture3D:
+ glFramebufferTexture3D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
+ SurfaceTargetToGL(src_params.target),
+ src_surface->Texture().handle, 0, 0);
+ glFramebufferTexture3D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
+ SurfaceTargetToGL(src_params.target), 0, 0, 0);
+ break;
+ default:
+ glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
+ GL_TEXTURE_2D, src_surface->Texture().handle, 0);
+ glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
+ 0, 0);
+ break;
+ }
+
+ switch (dst_params.target) {
+ case SurfaceParams::SurfaceTarget::Texture2D:
+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
+ GL_TEXTURE_2D, dst_surface->Texture().handle, 0);
+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
+ 0, 0);
+ break;
+ case SurfaceParams::SurfaceTarget::TextureCubemap:
+ glFramebufferTexture2D(
+ GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
+ static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face),
+ dst_surface->Texture().handle, 0);
+ glFramebufferTexture2D(
+ GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
+ static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cubemap_face), 0, 0);
+ break;
+ case SurfaceParams::SurfaceTarget::Texture2DArray:
+ glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
+ dst_surface->Texture().handle, 0, 0);
+ glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, 0, 0, 0);
+ break;
- glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex,
- 0);
- glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
- 0);
+ case SurfaceParams::SurfaceTarget::Texture3D:
+ glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
+ SurfaceTargetToGL(dst_params.target),
+ dst_surface->Texture().handle, 0, 0);
+ glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
+ SurfaceTargetToGL(dst_params.target), 0, 0, 0);
+ break;
+ default:
+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
+ GL_TEXTURE_2D, dst_surface->Texture().handle, 0);
+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
+ 0, 0);
+ break;
+ }
buffers = GL_COLOR_BUFFER_BIT;
- } else if (type == SurfaceType::Depth) {
- glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
- glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, src_tex, 0);
+ } else if (src_params.type == SurfaceType::Depth) {
+ glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
+ GL_TEXTURE_2D, 0, 0);
+ glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
+ src_surface->Texture().handle, 0);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
- glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
- glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, dst_tex, 0);
+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
+ GL_TEXTURE_2D, 0, 0);
+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
+ dst_surface->Texture().handle, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
buffers = GL_DEPTH_BUFFER_BIT;
- } else if (type == SurfaceType::DepthStencil) {
- glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
+ } else if (src_params.type == SurfaceType::DepthStencil) {
+ glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + src_attachment,
+ GL_TEXTURE_2D, 0, 0);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
- src_tex, 0);
+ src_surface->Texture().handle, 0);
- glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
+ glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + dst_attachment,
+ GL_TEXTURE_2D, 0, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
- dst_tex, 0);
+ dst_surface->Texture().handle, 0);
buffers = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
}
- glBlitFramebuffer(src_rect.left, src_rect.bottom, src_rect.right, src_rect.top, dst_rect.left,
- dst_rect.bottom, dst_rect.right, dst_rect.top, buffers,
+ const auto& rect{src_params.GetRect()};
+ glBlitFramebuffer(rect.left, rect.bottom, rect.right, rect.top, rect.left, rect.bottom,
+ rect.right, rect.top, buffers,
buffers == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST);
return true;
}
+static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
+ GLuint copy_pbo_handle, GLenum src_attachment = 0,
+ GLenum dst_attachment = 0, std::size_t cubemap_face = 0) {
+ ASSERT_MSG(dst_attachment == 0, "Unimplemented");
+
+ const auto& src_params{src_surface->GetSurfaceParams()};
+ const auto& dst_params{dst_surface->GetSurfaceParams()};
+
+ auto source_format = GetFormatTuple(src_params.pixel_format, src_params.component_type);
+ auto dest_format = GetFormatTuple(dst_params.pixel_format, dst_params.component_type);
+
+ std::size_t buffer_size =
+ std::max(src_params.size_in_bytes_total, dst_params.size_in_bytes_total);
+
+ glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo_handle);
+ glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB);
+ if (source_format.compressed) {
+ glGetCompressedTextureImage(src_surface->Texture().handle, src_attachment,
+ static_cast<GLsizei>(src_params.size_in_bytes_total), nullptr);
+ } else {
+ glGetTextureImage(src_surface->Texture().handle, src_attachment, source_format.format,
+ source_format.type, static_cast<GLsizei>(src_params.size_in_bytes_total),
+ nullptr);
+ }
+ // If the new texture is bigger than the previous one, we need to fill in the rest with data
+ // from the CPU.
+ if (src_params.size_in_bytes_total < dst_params.size_in_bytes_total) {
+ // Upload the rest of the memory.
+ if (dst_params.is_tiled) {
+ // TODO(Subv): We might have to de-tile the subtexture and re-tile it with the rest
+ // of the data in this case. Games like Super Mario Odyssey seem to hit this case
+ // when drawing, it re-uses the memory of a previous texture as a bigger framebuffer
+ // but it doesn't clear it beforehand, the texture is already full of zeros.
+ LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during "
+ "reinterpretation but the texture is tiled.");
+ }
+ std::size_t remaining_size =
+ dst_params.size_in_bytes_total - src_params.size_in_bytes_total;
+ std::vector<u8> data(remaining_size);
+ Memory::ReadBlock(dst_params.addr + src_params.size_in_bytes_total, data.data(),
+ data.size());
+ glBufferSubData(GL_PIXEL_PACK_BUFFER, src_params.size_in_bytes_total, remaining_size,
+ data.data());
+ }
+
+ glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
+
+ const GLsizei width{static_cast<GLsizei>(
+ std::min(src_params.GetRect().GetWidth(), dst_params.GetRect().GetWidth()))};
+ const GLsizei height{static_cast<GLsizei>(
+ std::min(src_params.GetRect().GetHeight(), dst_params.GetRect().GetHeight()))};
+
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, copy_pbo_handle);
+ if (dest_format.compressed) {
+ LOG_CRITICAL(HW_GPU, "Compressed copy is unimplemented!");
+ UNREACHABLE();
+ } else {
+ switch (dst_params.target) {
+ case SurfaceParams::SurfaceTarget::Texture1D:
+ glTextureSubImage1D(dst_surface->Texture().handle, 0, 0, width, dest_format.format,
+ dest_format.type, nullptr);
+ break;
+ case SurfaceParams::SurfaceTarget::Texture2D:
+ glTextureSubImage2D(dst_surface->Texture().handle, 0, 0, 0, width, height,
+ dest_format.format, dest_format.type, nullptr);
+ break;
+ case SurfaceParams::SurfaceTarget::Texture3D:
+ case SurfaceParams::SurfaceTarget::Texture2DArray:
+ glTextureSubImage3D(dst_surface->Texture().handle, 0, 0, 0, 0, width, height,
+ static_cast<GLsizei>(dst_params.depth), dest_format.format,
+ dest_format.type, nullptr);
+ break;
+ case SurfaceParams::SurfaceTarget::TextureCubemap:
+ glTextureSubImage3D(dst_surface->Texture().handle, 0, 0, 0,
+ static_cast<GLint>(cubemap_face), width, height, 1,
+ dest_format.format, dest_format.type, nullptr);
+ break;
+ default:
+ LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
+ static_cast<u32>(dst_params.target));
+ UNREACHABLE();
+ }
+ glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+ }
+}
+
CachedSurface::CachedSurface(const SurfaceParams& params)
: params(params), gl_target(SurfaceTargetToGL(params.target)) {
texture.Create();
@@ -481,6 +672,7 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
rect.GetWidth());
break;
case SurfaceParams::SurfaceTarget::Texture2D:
+ case SurfaceParams::SurfaceTarget::TextureCubemap:
glTexStorage2D(SurfaceTargetToGL(params.target), 1, format_tuple.internal_format,
rect.GetWidth(), rect.GetHeight());
break;
@@ -501,6 +693,9 @@ CachedSurface::CachedSurface(const SurfaceParams& params)
glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ VideoCore::LabelGLObject(GL_TEXTURE, texture.handle, params.addr,
+ SurfaceParams::SurfaceTargetName(params.target));
}
static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
@@ -582,29 +777,39 @@ void CachedSurface::LoadGLBuffer() {
const u32 bytes_per_pixel = GetGLBytesPerPixel(params.pixel_format);
const u32 copy_size = params.width * params.height * bytes_per_pixel;
+ const std::size_t total_size = copy_size * params.depth;
MICROPROFILE_SCOPE(OpenGL_SurfaceLoad);
if (params.is_tiled) {
+ gl_buffer.resize(total_size);
+
// TODO(bunnei): This only unswizzles and copies a 2D texture - we do not yet know how to do
// this for 3D textures, etc.
switch (params.target) {
case SurfaceParams::SurfaceTarget::Texture2D:
// Pass impl. to the fallback code below
break;
+ case SurfaceParams::SurfaceTarget::Texture2DArray:
+ case SurfaceParams::SurfaceTarget::TextureCubemap:
+ for (std::size_t index = 0; index < params.depth; ++index) {
+ const std::size_t offset{index * copy_size};
+ morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)](
+ params.width, params.block_height, params.height, gl_buffer.data() + offset,
+ copy_size, params.addr + offset);
+ }
+ break;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented tiled load for target={}",
static_cast<u32>(params.target));
UNREACHABLE();
}
- gl_buffer.resize(static_cast<std::size_t>(params.depth) * copy_size);
morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)](
params.width, params.block_height, params.height, gl_buffer.data(), copy_size,
params.addr);
} else {
- const u8* const texture_src_data_end{texture_src_data +
- (static_cast<std::size_t>(params.depth) * copy_size)};
+ const u8* const texture_src_data_end{texture_src_data + total_size};
gl_buffer.assign(texture_src_data, texture_src_data_end);
}
@@ -631,7 +836,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
// Load data from memory to the surface
const GLint x0 = static_cast<GLint>(rect.left);
const GLint y0 = static_cast<GLint>(rect.bottom);
- const std::size_t buffer_offset =
+ std::size_t buffer_offset =
static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.width +
static_cast<std::size_t>(x0)) *
GetGLBytesPerPixel(params.pixel_format);
@@ -660,15 +865,25 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
glCompressedTexImage2D(
SurfaceTargetToGL(params.target), 0, tuple.internal_format,
static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height), 0,
- static_cast<GLsizei>(params.size_in_bytes), &gl_buffer[buffer_offset]);
+ static_cast<GLsizei>(params.size_in_bytes_2d), &gl_buffer[buffer_offset]);
break;
case SurfaceParams::SurfaceTarget::Texture3D:
case SurfaceParams::SurfaceTarget::Texture2DArray:
glCompressedTexImage3D(
SurfaceTargetToGL(params.target), 0, tuple.internal_format,
static_cast<GLsizei>(params.width), static_cast<GLsizei>(params.height),
- static_cast<GLsizei>(params.depth), 0, static_cast<GLsizei>(params.size_in_bytes),
- &gl_buffer[buffer_offset]);
+ static_cast<GLsizei>(params.depth), 0,
+ static_cast<GLsizei>(params.size_in_bytes_total), &gl_buffer[buffer_offset]);
+ break;
+ case SurfaceParams::SurfaceTarget::TextureCubemap:
+ for (std::size_t face = 0; face < params.depth; ++face) {
+ glCompressedTexImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face),
+ 0, tuple.internal_format, static_cast<GLsizei>(params.width),
+ static_cast<GLsizei>(params.height), 0,
+ static_cast<GLsizei>(params.size_in_bytes_2d),
+ &gl_buffer[buffer_offset]);
+ buffer_offset += params.size_in_bytes_2d;
+ }
break;
default:
LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
@@ -676,8 +891,8 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
UNREACHABLE();
glCompressedTexImage2D(
GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width),
- static_cast<GLsizei>(params.height), 0, static_cast<GLsizei>(params.size_in_bytes),
- &gl_buffer[buffer_offset]);
+ static_cast<GLsizei>(params.height), 0,
+ static_cast<GLsizei>(params.size_in_bytes_2d), &gl_buffer[buffer_offset]);
}
} else {
@@ -700,6 +915,15 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format,
tuple.type, &gl_buffer[buffer_offset]);
break;
+ case SurfaceParams::SurfaceTarget::TextureCubemap:
+ for (std::size_t face = 0; face < params.depth; ++face) {
+ glTexSubImage2D(static_cast<GLenum>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face), 0, x0,
+ y0, static_cast<GLsizei>(rect.GetWidth()),
+ static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
+ &gl_buffer[buffer_offset]);
+ buffer_offset += params.size_in_bytes_2d;
+ }
+ break;
default:
LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
static_cast<u32>(params.target));
@@ -719,8 +943,9 @@ RasterizerCacheOpenGL::RasterizerCacheOpenGL() {
copy_pbo.Create();
}
-Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextureInfo& config) {
- return GetSurface(SurfaceParams::CreateForTexture(config));
+Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextureInfo& config,
+ const GLShader::SamplerEntry& entry) {
+ return GetSurface(SurfaceParams::CreateForTexture(config, entry));
}
Surface RasterizerCacheOpenGL::GetDepthBufferSurface(bool preserve_contents) {
@@ -808,98 +1033,69 @@ Surface RasterizerCacheOpenGL::GetUncachedSurface(const SurfaceParams& params) {
return surface;
}
-Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
+Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
const SurfaceParams& new_params) {
// Verify surface is compatible for blitting
- const auto& params{surface->GetSurfaceParams()};
+ auto old_params{old_surface->GetSurfaceParams()};
// Get a new surface with the new parameters, and blit the previous surface to it
Surface new_surface{GetUncachedSurface(new_params)};
- if (params.pixel_format == new_params.pixel_format ||
- !Settings::values.use_accurate_framebuffers) {
- // If the format is the same, just do a framebuffer blit. This is significantly faster than
- // using PBOs. The is also likely less accurate, as textures will be converted rather than
- // reinterpreted.
-
- BlitTextures(surface->Texture().handle, params.GetRect(), new_surface->Texture().handle,
- params.GetRect(), params.type, read_framebuffer.handle,
- draw_framebuffer.handle);
- } else {
- // When use_accurate_framebuffers setting is enabled, perform a more accurate surface copy,
- // where pixels are reinterpreted as a new format (without conversion). This code path uses
- // OpenGL PBOs and is quite slow.
-
- auto source_format = GetFormatTuple(params.pixel_format, params.component_type);
- auto dest_format = GetFormatTuple(new_params.pixel_format, new_params.component_type);
+ // If the format is the same, just do a framebuffer blit. This is significantly faster than
+ // using PBOs. The is also likely less accurate, as textures will be converted rather than
+ // reinterpreted. When use_accurate_framebuffers setting is enabled, perform a more accurate
+ // surface copy, where pixels are reinterpreted as a new format (without conversion). This
+ // code path uses OpenGL PBOs and is quite slow.
+ const bool is_blit{old_params.pixel_format == new_params.pixel_format ||
+ !Settings::values.use_accurate_framebuffers};
- std::size_t buffer_size = std::max(params.SizeInBytes(), new_params.SizeInBytes());
-
- glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo.handle);
- glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB);
- if (source_format.compressed) {
- glGetCompressedTextureImage(surface->Texture().handle, 0,
- static_cast<GLsizei>(params.SizeInBytes()), nullptr);
+ switch (new_params.target) {
+ case SurfaceParams::SurfaceTarget::Texture2D:
+ if (is_blit) {
+ BlitSurface(old_surface, new_surface, read_framebuffer.handle, draw_framebuffer.handle);
} else {
- glGetTextureImage(surface->Texture().handle, 0, source_format.format,
- source_format.type, static_cast<GLsizei>(params.SizeInBytes()),
- nullptr);
+ CopySurface(old_surface, new_surface, copy_pbo.handle);
}
- // If the new texture is bigger than the previous one, we need to fill in the rest with data
- // from the CPU.
- if (params.SizeInBytes() < new_params.SizeInBytes()) {
- // Upload the rest of the memory.
- if (new_params.is_tiled) {
- // TODO(Subv): We might have to de-tile the subtexture and re-tile it with the rest
- // of the data in this case. Games like Super Mario Odyssey seem to hit this case
- // when drawing, it re-uses the memory of a previous texture as a bigger framebuffer
- // but it doesn't clear it beforehand, the texture is already full of zeros.
- LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during "
- "reinterpretation but the texture is tiled.");
- }
- std::size_t remaining_size = new_params.SizeInBytes() - params.SizeInBytes();
- std::vector<u8> data(remaining_size);
- Memory::ReadBlock(new_params.addr + params.SizeInBytes(), data.data(), data.size());
- glBufferSubData(GL_PIXEL_PACK_BUFFER, params.SizeInBytes(), remaining_size,
- data.data());
+ break;
+ case SurfaceParams::SurfaceTarget::TextureCubemap: {
+ if (old_params.rt.array_mode != 1) {
+ // TODO(bunnei): This is used by Breath of the Wild, I'm not sure how to implement this
+ // yet (array rendering used as a cubemap texture).
+ LOG_CRITICAL(HW_GPU, "Unhandled rendertarget array_mode {}", old_params.rt.array_mode);
+ UNREACHABLE();
+ return new_surface;
}
- glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
-
- const auto& dest_rect{new_params.GetRect()};
-
- glBindBuffer(GL_PIXEL_UNPACK_BUFFER, copy_pbo.handle);
- if (dest_format.compressed) {
- LOG_CRITICAL(HW_GPU, "Compressed copy is unimplemented!");
- UNREACHABLE();
- } else {
- switch (new_params.target) {
- case SurfaceParams::SurfaceTarget::Texture1D:
- glTextureSubImage1D(new_surface->Texture().handle, 0, 0,
- static_cast<GLsizei>(dest_rect.GetWidth()), dest_format.format,
- dest_format.type, nullptr);
- break;
- case SurfaceParams::SurfaceTarget::Texture2D:
- glTextureSubImage2D(new_surface->Texture().handle, 0, 0, 0,
- static_cast<GLsizei>(dest_rect.GetWidth()),
- static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format,
- dest_format.type, nullptr);
- break;
- case SurfaceParams::SurfaceTarget::Texture3D:
- case SurfaceParams::SurfaceTarget::Texture2DArray:
- glTextureSubImage3D(new_surface->Texture().handle, 0, 0, 0, 0,
- static_cast<GLsizei>(dest_rect.GetWidth()),
- static_cast<GLsizei>(dest_rect.GetHeight()),
- static_cast<GLsizei>(new_params.depth), dest_format.format,
- dest_format.type, nullptr);
- break;
- default:
- LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
- static_cast<u32>(params.target));
- UNREACHABLE();
+ // This seems to be used for render-to-cubemap texture
+ ASSERT_MSG(old_params.target == SurfaceParams::SurfaceTarget::Texture2D, "Unexpected");
+ ASSERT_MSG(old_params.pixel_format == new_params.pixel_format, "Unexpected");
+ ASSERT_MSG(old_params.rt.base_layer == 0, "Unimplemented");
+
+ // TODO(bunnei): Verify the below - this stride seems to be in 32-bit words, not pixels.
+ // Tested with Splatoon 2, Super Mario Odyssey, and Breath of the Wild.
+ const std::size_t byte_stride{old_params.rt.layer_stride * sizeof(u32)};
+
+ for (std::size_t index = 0; index < new_params.depth; ++index) {
+ Surface face_surface{TryGetReservedSurface(old_params)};
+ ASSERT_MSG(face_surface, "Unexpected");
+
+ if (is_blit) {
+ BlitSurface(face_surface, new_surface, read_framebuffer.handle,
+ draw_framebuffer.handle, face_surface->GetSurfaceParams().rt.index,
+ new_params.rt.index, index);
+ } else {
+ CopySurface(face_surface, new_surface, copy_pbo.handle,
+ face_surface->GetSurfaceParams().rt.index, new_params.rt.index, index);
}
+
+ old_params.addr += byte_stride;
}
- glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
+ break;
+ }
+ default:
+ LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
+ static_cast<u32>(new_params.target));
+ UNREACHABLE();
}
return new_surface;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index d7a4bc37f..49025a3fe 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -9,12 +9,14 @@
#include <memory>
#include <vector>
+#include "common/alignment.h"
#include "common/common_types.h"
#include "common/hash.h"
#include "common/math_util.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/rasterizer_cache.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
+#include "video_core/renderer_opengl/gl_shader_gen.h"
#include "video_core/textures/texture.h"
namespace OpenGL {
@@ -126,6 +128,8 @@ struct SurfaceParams {
case Tegra::Texture::TextureType::Texture2D:
case Tegra::Texture::TextureType::Texture2DNoMipmap:
return SurfaceTarget::Texture2D;
+ case Tegra::Texture::TextureType::TextureCubemap:
+ return SurfaceTarget::TextureCubemap;
case Tegra::Texture::TextureType::Texture1DArray:
return SurfaceTarget::Texture1DArray;
case Tegra::Texture::TextureType::Texture2DArray:
@@ -137,6 +141,27 @@ struct SurfaceParams {
}
}
+ static std::string SurfaceTargetName(SurfaceTarget target) {
+ switch (target) {
+ case SurfaceTarget::Texture1D:
+ return "Texture1D";
+ case SurfaceTarget::Texture2D:
+ return "Texture2D";
+ case SurfaceTarget::Texture3D:
+ return "Texture3D";
+ case SurfaceTarget::Texture1DArray:
+ return "Texture1DArray";
+ case SurfaceTarget::Texture2DArray:
+ return "Texture2DArray";
+ case SurfaceTarget::TextureCubemap:
+ return "TextureCubemap";
+ default:
+ LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target));
+ UNREACHABLE();
+ return fmt::format("TextureUnknown({})", static_cast<u32>(target));
+ }
+ }
+
/**
* Gets the compression factor for the specified PixelFormat. This applies to just the
* "compressed width" and "compressed height", not the overall compression factor of a
@@ -668,17 +693,23 @@ struct SurfaceParams {
/// Returns the rectangle corresponding to this surface
MathUtil::Rectangle<u32> GetRect() const;
- /// Returns the size of this surface in bytes, adjusted for compression
- std::size_t SizeInBytes() const {
+ /// Returns the size of this surface as a 2D texture in bytes, adjusted for compression
+ std::size_t SizeInBytes2D() const {
const u32 compression_factor{GetCompressionFactor(pixel_format)};
ASSERT(width % compression_factor == 0);
ASSERT(height % compression_factor == 0);
return (width / compression_factor) * (height / compression_factor) *
- GetFormatBpp(pixel_format) * depth / CHAR_BIT;
+ GetFormatBpp(pixel_format) / CHAR_BIT;
+ }
+
+ /// Returns the total size of this surface in bytes, adjusted for compression
+ std::size_t SizeInBytesTotal() const {
+ return SizeInBytes2D() * depth;
}
/// Creates SurfaceParams from a texture configuration
- static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config);
+ static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config,
+ const GLShader::SamplerEntry& entry);
/// Creates SurfaceParams from a framebuffer configuration
static SurfaceParams CreateForFramebuffer(std::size_t index);
@@ -690,8 +721,9 @@ struct SurfaceParams {
/// Checks if surfaces are compatible for caching
bool IsCompatibleSurface(const SurfaceParams& other) const {
- return std::tie(pixel_format, type, width, height) ==
- std::tie(other.pixel_format, other.type, other.width, other.height);
+ return std::tie(pixel_format, type, width, height, target, depth) ==
+ std::tie(other.pixel_format, other.type, other.width, other.height, other.target,
+ other.depth);
}
VAddr addr;
@@ -704,8 +736,18 @@ struct SurfaceParams {
u32 height;
u32 depth;
u32 unaligned_height;
- std::size_t size_in_bytes;
+ std::size_t size_in_bytes_total;
+ std::size_t size_in_bytes_2d;
SurfaceTarget target;
+ u32 max_mip_level;
+
+ // Render target specific parameters, not used in caching
+ struct {
+ u32 index;
+ u32 array_mode;
+ u32 layer_stride;
+ u32 base_layer;
+ } rt;
};
}; // namespace OpenGL
@@ -715,6 +757,7 @@ struct SurfaceReserveKey : Common::HashableStruct<OpenGL::SurfaceParams> {
static SurfaceReserveKey Create(const OpenGL::SurfaceParams& params) {
SurfaceReserveKey res;
res.state = params;
+ res.state.rt = {}; // Ignore rt config in caching
return res;
}
};
@@ -738,7 +781,7 @@ public:
}
std::size_t GetSizeInBytes() const {
- return params.size_in_bytes;
+ return params.size_in_bytes_total;
}
const OGLTexture& Texture() const {
@@ -779,7 +822,8 @@ public:
RasterizerCacheOpenGL();
/// Get a surface based on the texture configuration
- Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config);
+ Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config,
+ const GLShader::SamplerEntry& entry);
/// Get the depth surface based on the framebuffer configuration
Surface GetDepthBufferSurface(bool preserve_contents);
@@ -801,7 +845,7 @@ private:
Surface GetUncachedSurface(const SurfaceParams& params);
/// Recreates a surface with new parameters
- Surface RecreateSurface(const Surface& surface, const SurfaceParams& new_params);
+ Surface RecreateSurface(const Surface& old_surface, const SurfaceParams& new_params);
/// Reserves a unique surface that can be reused later
void ReserveSurface(const Surface& surface);
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 894fe6eae..7cd8f91e4 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -8,6 +8,7 @@
#include "video_core/engines/maxwell_3d.h"
#include "video_core/renderer_opengl/gl_shader_cache.h"
#include "video_core/renderer_opengl/gl_shader_manager.h"
+#include "video_core/utils.h"
namespace OpenGL {
@@ -83,6 +84,7 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
shader.Create(program_result.first.c_str(), gl_type);
program.Create(true, shader.handle);
SetShaderUniformBlockBindings(program.handle);
+ VideoCore::LabelGLObject(GL_PROGRAM, program.handle, addr);
}
GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer) {
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 702ffbe9c..579a78702 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -2000,6 +2000,14 @@ private:
}
break;
}
+ case Tegra::Shader::TextureType::TextureCube: {
+ ASSERT_MSG(!is_array, "Unimplemented");
+ std::string x = regs.GetRegisterAsFloat(instr.gpr8);
+ std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
+ std::string z = regs.GetRegisterAsFloat(instr.gpr20);
+ coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
+ break;
+ }
default:
LOG_CRITICAL(HW_GPU, "Unhandled texture type {}",
static_cast<u32>(texture_type));
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index b86cd96e8..3de15ba9b 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -11,9 +11,6 @@
namespace OpenGL::GLShader {
-/// Number of OpenGL texture samplers that can be used in the fragment shader
-static constexpr std::size_t NumTextureSamplers = 32;
-
using Tegra::Engines::Maxwell3D;
/// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index af99132ba..1fe26a2a9 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -4,6 +4,7 @@
#include <iterator>
#include <glad/glad.h>
+#include "common/assert.h"
#include "common/logging/log.h"
#include "video_core/renderer_opengl/gl_state.h"
@@ -78,6 +79,8 @@ OpenGLState::OpenGLState() {
viewport.height = 0;
clip_distance = {};
+
+ point.size = 1;
}
void OpenGLState::Apply() const {
@@ -204,9 +207,6 @@ void OpenGLState::Apply() const {
glActiveTexture(TextureUnits::MaxwellTexture(static_cast<int>(i)).Enum());
glBindTexture(texture_unit.target, texture_unit.texture);
}
- if (texture_unit.sampler != cur_state_texture_unit.sampler) {
- glBindSampler(static_cast<GLuint>(i), texture_unit.sampler);
- }
// Update the texture swizzle
if (texture_unit.swizzle.r != cur_state_texture_unit.swizzle.r ||
texture_unit.swizzle.g != cur_state_texture_unit.swizzle.g ||
@@ -218,6 +218,27 @@ void OpenGLState::Apply() const {
}
}
+ // Samplers
+ {
+ bool has_delta{};
+ std::size_t first{}, last{};
+ std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> samplers;
+ for (std::size_t i = 0; i < std::size(samplers); ++i) {
+ samplers[i] = texture_units[i].sampler;
+ if (samplers[i] != cur_state.texture_units[i].sampler) {
+ if (!has_delta) {
+ first = i;
+ has_delta = true;
+ }
+ last = i;
+ }
+ }
+ if (has_delta) {
+ glBindSamplers(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1),
+ samplers.data());
+ }
+ }
+
// Framebuffer
if (draw.read_framebuffer != cur_state.draw.read_framebuffer) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer);
@@ -282,6 +303,11 @@ void OpenGLState::Apply() const {
}
}
+ // Point
+ if (point.size != cur_state.point.size) {
+ glPointSize(point.size);
+ }
+
cur_state = *this;
}
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index e3e24b9e7..dc21a2ee3 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -6,13 +6,10 @@
#include <array>
#include <glad/glad.h>
-
#include "video_core/engines/maxwell_3d.h"
namespace OpenGL {
-using Regs = Tegra::Engines::Maxwell3D::Regs;
-
namespace TextureUnits {
struct TextureUnit {
@@ -118,7 +115,7 @@ public:
target = GL_TEXTURE_2D;
}
};
- std::array<TextureUnit, 32> texture_units;
+ std::array<TextureUnit, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> texture_units;
struct {
GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING
@@ -145,6 +142,10 @@ public:
GLsizei height;
} viewport;
+ struct {
+ float size; // GL_POINT_SIZE
+ } point;
+
std::array<bool, 2> clip_distance; // GL_CLIP_DISTANCE
OpenGLState();
diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.cpp b/src/video_core/renderer_opengl/gl_stream_buffer.cpp
index 664f3ca20..e409228cc 100644
--- a/src/video_core/renderer_opengl/gl_stream_buffer.cpp
+++ b/src/video_core/renderer_opengl/gl_stream_buffer.cpp
@@ -74,7 +74,7 @@ std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr a
}
}
- if (invalidate | !persistent) {
+ if (invalidate || !persistent) {
GLbitfield flags = GL_MAP_WRITE_BIT | (persistent ? GL_MAP_PERSISTENT_BIT : 0) |
(coherent ? GL_MAP_COHERENT_BIT : GL_MAP_FLUSH_EXPLICIT_BIT) |
(invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : GL_MAP_UNSYNCHRONIZED_BIT);
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp
index 20ba6d4f6..3d5476e5d 100644
--- a/src/video_core/textures/decoders.cpp
+++ b/src/video_core/textures/decoders.cpp
@@ -13,47 +13,20 @@
namespace Tegra::Texture {
/**
+ * This table represents the internal swizzle of a gob,
+ * in format 16 bytes x 2 sector packing.
* Calculates the offset of an (x, y) position within a swizzled texture.
- * Taken from the Tegra X1 TRM.
+ * Taken from the Tegra X1 Technical Reference Manual. pages 1187-1188
*/
-static u32 GetSwizzleOffset(u32 x, u32 y, u32 image_width, u32 bytes_per_pixel, u32 block_height) {
- // Round up to the next gob
- const u32 image_width_in_gobs{(image_width * bytes_per_pixel + 63) / 64};
-
- u32 GOB_address = 0 + (y / (8 * block_height)) * 512 * block_height * image_width_in_gobs +
- (x * bytes_per_pixel / 64) * 512 * block_height +
- (y % (8 * block_height) / 8) * 512;
- x *= bytes_per_pixel;
- u32 address = GOB_address + ((x % 64) / 32) * 256 + ((y % 8) / 2) * 64 + ((x % 32) / 16) * 32 +
- (y % 2) * 16 + (x % 16);
-
- return address;
-}
-
-void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel,
- u8* swizzled_data, u8* unswizzled_data, bool unswizzle, u32 block_height) {
- u8* data_ptrs[2];
- for (unsigned y = 0; y < height; ++y) {
- for (unsigned x = 0; x < width; ++x) {
- u32 swizzle_offset = GetSwizzleOffset(x, y, width, bytes_per_pixel, block_height);
- u32 pixel_index = (x + y * width) * out_bytes_per_pixel;
-
- data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
- data_ptrs[!unswizzle] = &unswizzled_data[pixel_index];
-
- std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel);
- }
- }
-}
-
-template <std::size_t N, std::size_t M>
+template <std::size_t N, std::size_t M, u32 Align>
struct alignas(64) SwizzleTable {
+ static_assert(M * Align == 64, "Swizzle Table does not align to GOB");
constexpr SwizzleTable() {
for (u32 y = 0; y < N; ++y) {
for (u32 x = 0; x < M; ++x) {
- const u32 x2 = x * 16;
+ const u32 x2 = x * Align;
values[y][x] = static_cast<u16>(((x2 % 64) / 32) * 256 + ((y % 8) / 2) * 64 +
- ((x2 % 32) / 16) * 32 + (y % 2) * 16);
+ ((x2 % 32) / 16) * 32 + (y % 2) * 16 + (x2 % 16));
}
}
}
@@ -63,24 +36,60 @@ struct alignas(64) SwizzleTable {
std::array<std::array<u16, M>, N> values{};
};
-constexpr auto swizzle_table = SwizzleTable<8, 4>();
+constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>();
+constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>();
-void FastSwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u8* swizzled_data,
- u8* unswizzled_data, bool unswizzle, u32 block_height) {
+static void LegacySwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel,
+ u8* swizzled_data, u8* unswizzled_data, bool unswizzle,
+ u32 block_height) {
+ std::array<u8*, 2> data_ptrs;
+ const std::size_t stride = width * bytes_per_pixel;
+ const std::size_t gobs_in_x = 64;
+ const std::size_t gobs_in_y = 8;
+ const std::size_t gobs_size = gobs_in_x * gobs_in_y;
+ const std::size_t image_width_in_gobs{(stride + gobs_in_x - 1) / gobs_in_x};
+ for (std::size_t y = 0; y < height; ++y) {
+ const std::size_t gob_y_address =
+ (y / (gobs_in_y * block_height)) * gobs_size * block_height * image_width_in_gobs +
+ (y % (gobs_in_y * block_height) / gobs_in_y) * gobs_size;
+ const auto& table = legacy_swizzle_table[y % gobs_in_y];
+ for (std::size_t x = 0; x < width; ++x) {
+ const std::size_t gob_address =
+ gob_y_address + (x * bytes_per_pixel / gobs_in_x) * gobs_size * block_height;
+ const std::size_t x2 = x * bytes_per_pixel;
+ const std::size_t swizzle_offset = gob_address + table[x2 % gobs_in_x];
+ const std::size_t pixel_index = (x + y * width) * out_bytes_per_pixel;
+
+ data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
+ data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
+
+ std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel);
+ }
+ }
+}
+
+static void FastSwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel,
+ u8* swizzled_data, u8* unswizzled_data, bool unswizzle,
+ u32 block_height) {
std::array<u8*, 2> data_ptrs;
const std::size_t stride{width * bytes_per_pixel};
- const std::size_t image_width_in_gobs{(stride + 63) / 64};
+ const std::size_t gobs_in_x = 64;
+ const std::size_t gobs_in_y = 8;
+ const std::size_t gobs_size = gobs_in_x * gobs_in_y;
+ const std::size_t image_width_in_gobs{(stride + gobs_in_x - 1) / gobs_in_x};
const std::size_t copy_size{16};
for (std::size_t y = 0; y < height; ++y) {
const std::size_t initial_gob =
- (y / (8 * block_height)) * 512 * block_height * image_width_in_gobs +
- (y % (8 * block_height) / 8) * 512;
- const std::size_t pixel_base{y * width * bytes_per_pixel};
- const auto& table = swizzle_table[y % 8];
+ (y / (gobs_in_y * block_height)) * gobs_size * block_height * image_width_in_gobs +
+ (y % (gobs_in_y * block_height) / gobs_in_y) * gobs_size;
+ const std::size_t pixel_base{y * width * out_bytes_per_pixel};
+ const auto& table = fast_swizzle_table[y % gobs_in_y];
for (std::size_t xb = 0; xb < stride; xb += copy_size) {
- const std::size_t gob_address{initial_gob + (xb / 64) * 512 * block_height};
+ const std::size_t gob_address{initial_gob +
+ (xb / gobs_in_x) * gobs_size * block_height};
const std::size_t swizzle_offset{gob_address + table[(xb / 16) % 4]};
- const std::size_t pixel_index{xb + pixel_base};
+ const std::size_t out_x = xb * out_bytes_per_pixel / bytes_per_pixel;
+ const std::size_t pixel_index{out_x + pixel_base};
data_ptrs[unswizzle] = swizzled_data + swizzle_offset;
data_ptrs[!unswizzle] = unswizzled_data + pixel_index;
std::memcpy(data_ptrs[0], data_ptrs[1], copy_size);
@@ -88,6 +97,17 @@ void FastSwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u8* swizzled_da
}
}
+void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel,
+ u8* swizzled_data, u8* unswizzled_data, bool unswizzle, u32 block_height) {
+ if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) {
+ FastSwizzleData(width, height, bytes_per_pixel, out_bytes_per_pixel, swizzled_data,
+ unswizzled_data, unswizzle, block_height);
+ } else {
+ LegacySwizzleData(width, height, bytes_per_pixel, out_bytes_per_pixel, swizzled_data,
+ unswizzled_data, unswizzle, block_height);
+ }
+}
+
u32 BytesPerPixel(TextureFormat format) {
switch (format) {
case TextureFormat::DXT1:
@@ -134,13 +154,8 @@ u32 BytesPerPixel(TextureFormat format) {
std::vector<u8> UnswizzleTexture(VAddr address, u32 tile_size, u32 bytes_per_pixel, u32 width,
u32 height, u32 block_height) {
std::vector<u8> unswizzled_data(width * height * bytes_per_pixel);
- if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) {
- FastSwizzleData(width / tile_size, height / tile_size, bytes_per_pixel,
- Memory::GetPointer(address), unswizzled_data.data(), true, block_height);
- } else {
- CopySwizzledData(width / tile_size, height / tile_size, bytes_per_pixel, bytes_per_pixel,
- Memory::GetPointer(address), unswizzled_data.data(), true, block_height);
- }
+ CopySwizzledData(width / tile_size, height / tile_size, bytes_per_pixel, bytes_per_pixel,
+ Memory::GetPointer(address), unswizzled_data.data(), true, block_height);
return unswizzled_data;
}
diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h
index c2fb824b2..14aea4838 100644
--- a/src/video_core/textures/texture.h
+++ b/src/video_core/textures/texture.h
@@ -165,6 +165,8 @@ struct TICEntry {
// High 16 bits of the pitch value
BitField<0, 16, u32> pitch_high;
+
+ BitField<28, 4, u32> max_mip_level;
};
union {
BitField<0, 16, u32> width_minus_1;
diff --git a/src/video_core/utils.h b/src/video_core/utils.h
index e0a14d48f..681919ae3 100644
--- a/src/video_core/utils.h
+++ b/src/video_core/utils.h
@@ -161,4 +161,26 @@ static inline void MortonCopyPixels128(u32 width, u32 height, u32 bytes_per_pixe
}
}
+static void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr,
+ std::string extra_info = "") {
+ if (!GLAD_GL_KHR_debug) {
+ return; // We don't need to throw an error as this is just for debugging
+ }
+ const std::string nice_addr = fmt::format("0x{:016x}", addr);
+ std::string object_label;
+
+ switch (identifier) {
+ case GL_TEXTURE:
+ object_label = extra_info + "@" + nice_addr;
+ break;
+ case GL_PROGRAM:
+ object_label = "ShaderProgram@" + nice_addr;
+ break;
+ default:
+ object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr);
+ break;
+ }
+ glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str()));
+}
+
} // namespace VideoCore