yuzu-emu
/
yuzu
Archived
1
0
Fork 0

Merge pull request #1256 from bunnei/tex-target-support

Initial support for non-2D textures
This commit is contained in:
bunnei 2018-09-08 16:14:46 -04:00 committed by GitHub
commit af074ee422
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 422 additions and 229 deletions

View File

@ -293,10 +293,6 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
tic_entry.header_version == Texture::TICHeaderVersion::Pitch, tic_entry.header_version == Texture::TICHeaderVersion::Pitch,
"TIC versions other than BlockLinear or Pitch are unimplemented"); "TIC versions other than BlockLinear or Pitch are unimplemented");
ASSERT_MSG((tic_entry.texture_type == Texture::TextureType::Texture2D) ||
(tic_entry.texture_type == Texture::TextureType::Texture2DNoMipmap),
"Texture types other than Texture2D are unimplemented");
auto r_type = tic_entry.r_type.Value(); auto r_type = tic_entry.r_type.Value();
auto g_type = tic_entry.g_type.Value(); auto g_type = tic_entry.g_type.Value();
auto b_type = tic_entry.b_type.Value(); auto b_type = tic_entry.b_type.Value();

View File

@ -586,7 +586,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
void RasterizerOpenGL::SamplerInfo::Create() { void RasterizerOpenGL::SamplerInfo::Create() {
sampler.Create(); sampler.Create();
mag_filter = min_filter = Tegra::Texture::TextureFilter::Linear; mag_filter = min_filter = Tegra::Texture::TextureFilter::Linear;
wrap_u = wrap_v = Tegra::Texture::WrapMode::Wrap; wrap_u = wrap_v = wrap_p = Tegra::Texture::WrapMode::Wrap;
// default is GL_LINEAR_MIPMAP_LINEAR // default is GL_LINEAR_MIPMAP_LINEAR
glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@ -613,8 +613,13 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
wrap_v = config.wrap_v; wrap_v = config.wrap_v;
glSamplerParameteri(s, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(wrap_v)); glSamplerParameteri(s, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(wrap_v));
} }
if (wrap_p != config.wrap_p) {
wrap_p = config.wrap_p;
glSamplerParameteri(s, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(wrap_p));
}
if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border) { if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border ||
wrap_p == Tegra::Texture::WrapMode::Border) {
const GLvec4 new_border_color = {{config.border_color_r, config.border_color_g, const GLvec4 new_border_color = {{config.border_color_r, config.border_color_g,
config.border_color_b, config.border_color_a}}; config.border_color_b, config.border_color_a}};
if (border_color != new_border_color) { if (border_color != new_border_color) {
@ -698,14 +703,15 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset()); const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset());
if (!texture.enabled) { if (!texture.enabled) {
state.texture_units[current_bindpoint].texture_2d = 0; state.texture_units[current_bindpoint].texture = 0;
continue; continue;
} }
texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc); texture_samplers[current_bindpoint].SyncWithConfig(texture.tsc);
Surface surface = res_cache.GetTextureSurface(texture); Surface surface = res_cache.GetTextureSurface(texture);
if (surface != nullptr) { if (surface != nullptr) {
state.texture_units[current_bindpoint].texture_2d = surface->Texture().handle; state.texture_units[current_bindpoint].texture = surface->Texture().handle;
state.texture_units[current_bindpoint].target = surface->Target();
state.texture_units[current_bindpoint].swizzle.r = state.texture_units[current_bindpoint].swizzle.r =
MaxwellToGL::SwizzleSource(texture.tic.x_source); MaxwellToGL::SwizzleSource(texture.tic.x_source);
state.texture_units[current_bindpoint].swizzle.g = state.texture_units[current_bindpoint].swizzle.g =
@ -716,7 +722,7 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
MaxwellToGL::SwizzleSource(texture.tic.w_source); MaxwellToGL::SwizzleSource(texture.tic.w_source);
} else { } else {
// Can occur when texture addr is null or its memory is unmapped/invalid // Can occur when texture addr is null or its memory is unmapped/invalid
state.texture_units[current_bindpoint].texture_2d = 0; state.texture_units[current_bindpoint].texture = 0;
} }
} }

View File

@ -93,6 +93,7 @@ private:
Tegra::Texture::TextureFilter min_filter; Tegra::Texture::TextureFilter min_filter;
Tegra::Texture::WrapMode wrap_u; Tegra::Texture::WrapMode wrap_u;
Tegra::Texture::WrapMode wrap_v; Tegra::Texture::WrapMode wrap_v;
Tegra::Texture::WrapMode wrap_p;
GLvec4 border_color; GLvec4 border_color;
}; };

View File

@ -7,6 +7,7 @@
#include "common/alignment.h" #include "common/alignment.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h"
#include "common/microprofile.h" #include "common/microprofile.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "core/core.h" #include "core/core.h"
@ -51,10 +52,12 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
params.type = GetFormatType(params.pixel_format); params.type = GetFormatType(params.pixel_format);
params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format)); params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format));
params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format)); params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
params.depth = config.tic.Depth();
params.unaligned_height = config.tic.Height(); params.unaligned_height = config.tic.Height();
params.size_in_bytes = params.SizeInBytes(); params.size_in_bytes = params.SizeInBytes();
params.cache_width = Common::AlignUp(params.width, 16); params.cache_width = Common::AlignUp(params.width, 8);
params.cache_height = Common::AlignUp(params.height, 16); params.cache_height = Common::AlignUp(params.height, 8);
params.target = SurfaceTargetFromTextureType(config.tic.texture_type);
return params; return params;
} }
@ -69,10 +72,12 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
params.type = GetFormatType(params.pixel_format); params.type = GetFormatType(params.pixel_format);
params.width = config.width; params.width = config.width;
params.height = config.height; params.height = config.height;
params.depth = 1;
params.unaligned_height = config.height; params.unaligned_height = config.height;
params.size_in_bytes = params.SizeInBytes(); params.size_in_bytes = params.SizeInBytes();
params.cache_width = Common::AlignUp(params.width, 16); params.cache_width = Common::AlignUp(params.width, 8);
params.cache_height = Common::AlignUp(params.height, 16); params.cache_height = Common::AlignUp(params.height, 8);
params.target = SurfaceTarget::Texture2D;
return params; return params;
} }
@ -86,13 +91,14 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
params.pixel_format = PixelFormatFromDepthFormat(format); params.pixel_format = PixelFormatFromDepthFormat(format);
params.component_type = ComponentTypeFromDepthFormat(format); params.component_type = ComponentTypeFromDepthFormat(format);
params.type = GetFormatType(params.pixel_format); params.type = GetFormatType(params.pixel_format);
params.size_in_bytes = params.SizeInBytes();
params.width = zeta_width; params.width = zeta_width;
params.height = zeta_height; params.height = zeta_height;
params.depth = 1;
params.unaligned_height = zeta_height; params.unaligned_height = zeta_height;
params.size_in_bytes = params.SizeInBytes(); params.size_in_bytes = params.SizeInBytes();
params.cache_width = Common::AlignUp(params.width, 16); params.cache_width = Common::AlignUp(params.width, 8);
params.cache_height = Common::AlignUp(params.height, 16); params.cache_height = Common::AlignUp(params.height, 8);
params.target = SurfaceTarget::Texture2D;
return params; return params;
} }
@ -166,6 +172,26 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
ComponentType::Float, false}, // Z32FS8 ComponentType::Float, false}, // Z32FS8
}}; }};
static GLenum SurfaceTargetToGL(SurfaceParams::SurfaceTarget target) {
switch (target) {
case SurfaceParams::SurfaceTarget::Texture1D:
return GL_TEXTURE_1D;
case SurfaceParams::SurfaceTarget::Texture2D:
return GL_TEXTURE_2D;
case SurfaceParams::SurfaceTarget::Texture3D:
return GL_TEXTURE_3D;
case SurfaceParams::SurfaceTarget::Texture1DArray:
return GL_TEXTURE_1D_ARRAY;
case SurfaceParams::SurfaceTarget::Texture2DArray:
return GL_TEXTURE_2D_ARRAY;
case SurfaceParams::SurfaceTarget::TextureCubemap:
return GL_TEXTURE_CUBE_MAP;
}
LOG_CRITICAL(Render_OpenGL, "Unimplemented texture target={}", static_cast<u32>(target));
UNREACHABLE();
return {};
}
static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) { static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) {
ASSERT(static_cast<size_t>(pixel_format) < tex_format_tuples.size()); ASSERT(static_cast<size_t>(pixel_format) < tex_format_tuples.size());
auto& format = tex_format_tuples[static_cast<unsigned int>(pixel_format)]; auto& format = tex_format_tuples[static_cast<unsigned int>(pixel_format)];
@ -220,7 +246,8 @@ static bool IsFormatBCn(PixelFormat format) {
} }
template <bool morton_to_gl, PixelFormat format> template <bool morton_to_gl, PixelFormat format>
void MortonCopy(u32 stride, u32 block_height, u32 height, std::vector<u8>& gl_buffer, VAddr addr) { void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, size_t gl_buffer_size,
VAddr addr) {
constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT; constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT;
constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format);
@ -230,18 +257,18 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, std::vector<u8>& gl_bu
const u32 tile_size{IsFormatBCn(format) ? 4U : 1U}; const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};
const std::vector<u8> data = Tegra::Texture::UnswizzleTexture( const std::vector<u8> data = Tegra::Texture::UnswizzleTexture(
addr, tile_size, bytes_per_pixel, stride, height, block_height); addr, tile_size, bytes_per_pixel, stride, height, block_height);
const size_t size_to_copy{std::min(gl_buffer.size(), data.size())}; const size_t size_to_copy{std::min(gl_buffer_size, data.size())};
gl_buffer.assign(data.begin(), data.begin() + size_to_copy); memcpy(gl_buffer, data.data(), size_to_copy);
} else { } else {
// TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should
// check the configuration for this and perform more generic un/swizzle // check the configuration for this and perform more generic un/swizzle
LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!"); LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!");
VideoCore::MortonCopyPixels128(stride, height, bytes_per_pixel, gl_bytes_per_pixel, VideoCore::MortonCopyPixels128(stride, height, bytes_per_pixel, gl_bytes_per_pixel,
Memory::GetPointer(addr), gl_buffer.data(), morton_to_gl); Memory::GetPointer(addr), gl_buffer, morton_to_gl);
} }
} }
static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr), static constexpr std::array<void (*)(u32, u32, u32, u8*, size_t, VAddr),
SurfaceParams::MaxPixelFormat> SurfaceParams::MaxPixelFormat>
morton_to_gl_fns = { morton_to_gl_fns = {
// clang-format off // clang-format off
@ -298,7 +325,7 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr),
// clang-format on // clang-format on
}; };
static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr), static constexpr std::array<void (*)(u32, u32, u32, u8*, size_t, VAddr),
SurfaceParams::MaxPixelFormat> SurfaceParams::MaxPixelFormat>
gl_to_morton_fns = { gl_to_morton_fns = {
// clang-format off // clang-format off
@ -357,33 +384,6 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr),
// clang-format on // clang-format on
}; };
// Allocate an uninitialized texture of appropriate size and format for the surface
static void AllocateSurfaceTexture(GLuint texture, const FormatTuple& format_tuple, u32 width,
u32 height) {
OpenGLState cur_state = OpenGLState::GetCurState();
// Keep track of previous texture bindings
GLuint old_tex = cur_state.texture_units[0].texture_2d;
cur_state.texture_units[0].texture_2d = texture;
cur_state.Apply();
glActiveTexture(GL_TEXTURE0);
if (!format_tuple.compressed) {
// Only pre-create the texture for non-compressed textures.
glTexImage2D(GL_TEXTURE_2D, 0, format_tuple.internal_format, width, height, 0,
format_tuple.format, format_tuple.type, nullptr);
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Restore previous texture bindings
cur_state.texture_units[0].texture_2d = old_tex;
cur_state.Apply();
}
static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect, GLuint dst_tex, static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect, GLuint dst_tex,
const MathUtil::Rectangle<u32>& dst_rect, SurfaceType type, const MathUtil::Rectangle<u32>& dst_rect, SurfaceType type,
GLuint read_fb_handle, GLuint draw_fb_handle) { GLuint read_fb_handle, GLuint draw_fb_handle) {
@ -438,12 +438,56 @@ static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rec
return true; return true;
} }
CachedSurface::CachedSurface(const SurfaceParams& params) : params(params) { CachedSurface::CachedSurface(const SurfaceParams& params)
: params(params), gl_target(SurfaceTargetToGL(params.target)) {
texture.Create(); texture.Create();
const auto& rect{params.GetRect()}; const auto& rect{params.GetRect()};
AllocateSurfaceTexture(texture.handle,
GetFormatTuple(params.pixel_format, params.component_type), // Keep track of previous texture bindings
rect.GetWidth(), rect.GetHeight()); OpenGLState cur_state = OpenGLState::GetCurState();
const auto& old_tex = cur_state.texture_units[0];
SCOPE_EXIT({
cur_state.texture_units[0] = old_tex;
cur_state.Apply();
});
cur_state.texture_units[0].texture = texture.handle;
cur_state.texture_units[0].target = SurfaceTargetToGL(params.target);
cur_state.Apply();
glActiveTexture(GL_TEXTURE0);
const auto& format_tuple = GetFormatTuple(params.pixel_format, params.component_type);
if (!format_tuple.compressed) {
// Only pre-create the texture for non-compressed textures.
switch (params.target) {
case SurfaceParams::SurfaceTarget::Texture1D:
glTexImage1D(SurfaceTargetToGL(params.target), 0, format_tuple.internal_format,
rect.GetWidth(), 0, format_tuple.format, format_tuple.type, nullptr);
break;
case SurfaceParams::SurfaceTarget::Texture2D:
glTexImage2D(SurfaceTargetToGL(params.target), 0, format_tuple.internal_format,
rect.GetWidth(), rect.GetHeight(), 0, format_tuple.format,
format_tuple.type, nullptr);
break;
case SurfaceParams::SurfaceTarget::Texture3D:
case SurfaceParams::SurfaceTarget::Texture2DArray:
glTexImage3D(SurfaceTargetToGL(params.target), 0, format_tuple.internal_format,
rect.GetWidth(), rect.GetHeight(), params.depth, 0, format_tuple.format,
format_tuple.type, nullptr);
break;
default:
LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
static_cast<u32>(params.target));
UNREACHABLE();
glTexImage2D(GL_TEXTURE_2D, 0, format_tuple.internal_format, rect.GetWidth(),
rect.GetHeight(), 0, format_tuple.format, format_tuple.type, nullptr);
}
}
glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MAX_LEVEL, 0);
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);
} }
static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) { static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) {
@ -514,23 +558,6 @@ static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelForma
} }
} }
/**
* Helper function to perform software conversion (as needed) when flushing a buffer to Switch
* memory. This is for Maxwell pixel formats that cannot be represented as-is in OpenGL or with
* typical desktop GPUs.
*/
static void ConvertFormatAsNeeded_FlushGLBuffer(std::vector<u8>& /*data*/, PixelFormat pixel_format,
u32 /*width*/, u32 /*height*/) {
switch (pixel_format) {
case PixelFormat::ASTC_2D_4X4:
case PixelFormat::S8Z24:
LOG_CRITICAL(Render_OpenGL, "Unimplemented pixel_format={}",
static_cast<u32>(pixel_format));
UNREACHABLE();
break;
}
}
MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192)); MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64, 192));
void CachedSurface::LoadGLBuffer() { void CachedSurface::LoadGLBuffer() {
ASSERT(params.type != SurfaceType::Fill); ASSERT(params.type != SurfaceType::Fill);
@ -545,13 +572,24 @@ void CachedSurface::LoadGLBuffer() {
MICROPROFILE_SCOPE(OpenGL_SurfaceLoad); MICROPROFILE_SCOPE(OpenGL_SurfaceLoad);
if (params.is_tiled) { if (params.is_tiled) {
gl_buffer.resize(copy_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;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented tiled load for target={}",
static_cast<u32>(params.target));
UNREACHABLE();
}
gl_buffer.resize(params.depth * copy_size);
morton_to_gl_fns[static_cast<size_t>(params.pixel_format)]( morton_to_gl_fns[static_cast<size_t>(params.pixel_format)](
params.width, params.block_height, params.height, gl_buffer, params.addr); params.width, params.block_height, params.height, gl_buffer.data(), copy_size,
params.addr);
} else { } else {
const u8* const texture_src_data_end = texture_src_data + copy_size; const u8* const texture_src_data_end{texture_src_data + (params.depth * copy_size)};
gl_buffer.assign(texture_src_data, texture_src_data_end); gl_buffer.assign(texture_src_data, texture_src_data_end);
} }
@ -560,23 +598,7 @@ void CachedSurface::LoadGLBuffer() {
MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64)); MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64));
void CachedSurface::FlushGLBuffer() { void CachedSurface::FlushGLBuffer() {
u8* const dst_buffer = Memory::GetPointer(params.addr); ASSERT_MSG(false, "Unimplemented");
ASSERT(dst_buffer);
ASSERT(gl_buffer.size() ==
params.width * params.height * GetGLBytesPerPixel(params.pixel_format));
MICROPROFILE_SCOPE(OpenGL_SurfaceFlush);
ConvertFormatAsNeeded_FlushGLBuffer(gl_buffer, params.pixel_format, params.width,
params.height);
if (!params.is_tiled) {
std::memcpy(dst_buffer, gl_buffer.data(), params.size_in_bytes);
} else {
gl_to_morton_fns[static_cast<size_t>(params.pixel_format)](
params.width, params.block_height, params.height, gl_buffer, params.addr);
}
} }
MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192)); MICROPROFILE_DEFINE(OpenGL_TextureUL, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192));
@ -587,7 +609,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
MICROPROFILE_SCOPE(OpenGL_TextureUL); MICROPROFILE_SCOPE(OpenGL_TextureUL);
ASSERT(gl_buffer.size() == ASSERT(gl_buffer.size() ==
params.width * params.height * GetGLBytesPerPixel(params.pixel_format)); params.width * params.height * GetGLBytesPerPixel(params.pixel_format) * params.depth);
const auto& rect{params.GetRect()}; const auto& rect{params.GetRect()};
@ -600,8 +622,13 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
GLuint target_tex = texture.handle; GLuint target_tex = texture.handle;
OpenGLState cur_state = OpenGLState::GetCurState(); OpenGLState cur_state = OpenGLState::GetCurState();
GLuint old_tex = cur_state.texture_units[0].texture_2d; const auto& old_tex = cur_state.texture_units[0];
cur_state.texture_units[0].texture_2d = target_tex; SCOPE_EXIT({
cur_state.texture_units[0] = old_tex;
cur_state.Apply();
});
cur_state.texture_units[0].texture = target_tex;
cur_state.texture_units[0].target = SurfaceTargetToGL(params.target);
cur_state.Apply(); cur_state.Apply();
// Ensure no bad interactions with GL_UNPACK_ALIGNMENT // Ensure no bad interactions with GL_UNPACK_ALIGNMENT
@ -610,74 +637,68 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
if (tuple.compressed) { if (tuple.compressed) {
switch (params.target) {
case SurfaceParams::SurfaceTarget::Texture2D:
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]);
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]);
break;
default:
LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
static_cast<u32>(params.target));
UNREACHABLE();
glCompressedTexImage2D( glCompressedTexImage2D(
GL_TEXTURE_2D, 0, tuple.internal_format, static_cast<GLsizei>(params.width), 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), static_cast<GLsizei>(params.height), 0, static_cast<GLsizei>(params.size_in_bytes),
&gl_buffer[buffer_offset]); &gl_buffer[buffer_offset]);
}
} else { } else {
switch (params.target) {
case SurfaceParams::SurfaceTarget::Texture1D:
glTexSubImage1D(SurfaceTargetToGL(params.target), 0, x0,
static_cast<GLsizei>(rect.GetWidth()), tuple.format, tuple.type,
&gl_buffer[buffer_offset]);
break;
case SurfaceParams::SurfaceTarget::Texture2D:
glTexSubImage2D(SurfaceTargetToGL(params.target), 0, x0, y0,
static_cast<GLsizei>(rect.GetWidth()),
static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
&gl_buffer[buffer_offset]);
break;
case SurfaceParams::SurfaceTarget::Texture3D:
case SurfaceParams::SurfaceTarget::Texture2DArray:
glTexSubImage3D(SurfaceTargetToGL(params.target), 0, x0, y0, 0,
static_cast<GLsizei>(rect.GetWidth()),
static_cast<GLsizei>(rect.GetHeight()), params.depth, tuple.format,
tuple.type, &gl_buffer[buffer_offset]);
break;
default:
LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}",
static_cast<u32>(params.target));
UNREACHABLE();
glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()), glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()),
static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type, static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
&gl_buffer[buffer_offset]); &gl_buffer[buffer_offset]);
} }
}
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
cur_state.texture_units[0].texture_2d = old_tex;
cur_state.Apply();
}
MICROPROFILE_DEFINE(OpenGL_TextureDL, "OpenGL", "Texture Download", MP_RGB(128, 192, 64));
void CachedSurface::DownloadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle) {
if (params.type == SurfaceType::Fill)
return;
MICROPROFILE_SCOPE(OpenGL_TextureDL);
gl_buffer.resize(params.width * params.height * GetGLBytesPerPixel(params.pixel_format));
OpenGLState state = OpenGLState::GetCurState();
OpenGLState prev_state = state;
SCOPE_EXIT({ prev_state.Apply(); });
const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type);
// Ensure no bad interactions with GL_PACK_ALIGNMENT
ASSERT(params.width * GetGLBytesPerPixel(params.pixel_format) % 4 == 0);
glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.width));
const auto& rect{params.GetRect()};
size_t buffer_offset =
(rect.bottom * params.width + rect.left) * GetGLBytesPerPixel(params.pixel_format);
state.UnbindTexture(texture.handle);
state.draw.read_framebuffer = read_fb_handle;
state.Apply();
if (params.type == SurfaceType::ColorTexture) {
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
texture.handle, 0);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
0);
} else if (params.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,
texture.handle, 0);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
} else {
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
texture.handle, 0);
}
glReadPixels(static_cast<GLint>(rect.left), static_cast<GLint>(rect.bottom),
static_cast<GLsizei>(rect.GetWidth()), static_cast<GLsizei>(rect.GetHeight()),
tuple.format, tuple.type, &gl_buffer[buffer_offset]);
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
} }
RasterizerCacheOpenGL::RasterizerCacheOpenGL() { RasterizerCacheOpenGL::RasterizerCacheOpenGL() {
read_framebuffer.Create(); read_framebuffer.Create();
draw_framebuffer.Create(); draw_framebuffer.Create();
copy_pbo.Create();
} }
Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextureInfo& config) { Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextureInfo& config) {
@ -748,7 +769,6 @@ void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) {
} }
void RasterizerCacheOpenGL::FlushSurface(const Surface& surface) { void RasterizerCacheOpenGL::FlushSurface(const Surface& surface) {
surface->DownloadGLTexture(read_framebuffer.handle, draw_framebuffer.handle);
surface->FlushGLBuffer(); surface->FlushGLBuffer();
} }
@ -809,8 +829,8 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
// If format is unchanged, we can do a faster blit without reinterpreting pixel data // If format is unchanged, we can do a faster blit without reinterpreting pixel data
if (params.pixel_format == new_params.pixel_format) { if (params.pixel_format == new_params.pixel_format) {
BlitTextures(surface->Texture().handle, params.GetRect(), new_surface->Texture().handle, BlitTextures(surface->Texture().handle, params.GetRect(), new_surface->Texture().handle,
new_surface->GetSurfaceParams().GetRect(), params.type, params.GetRect(), params.type, read_framebuffer.handle,
read_framebuffer.handle, draw_framebuffer.handle); draw_framebuffer.handle);
return new_surface; return new_surface;
} }
@ -821,12 +841,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
size_t buffer_size = std::max(params.SizeInBytes(), new_params.SizeInBytes()); size_t buffer_size = std::max(params.SizeInBytes(), new_params.SizeInBytes());
// Use a Pixel Buffer Object to download the previous texture and then upload it to the new glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo.handle);
// one using the new format.
OGLBuffer pbo;
pbo.Create();
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo.handle);
glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB); glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB);
if (source_format.compressed) { if (source_format.compressed) {
glGetCompressedTextureImage(surface->Texture().handle, 0, glGetCompressedTextureImage(surface->Texture().handle, 0,
@ -845,7 +860,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
// of the data in this case. Games like Super Mario Odyssey seem to hit this case // 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 // 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. // but it doesn't clear it beforehand, the texture is already full of zeros.
LOG_CRITICAL(HW_GPU, "Trying to upload extra texture data from the CPU during " LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during "
"reinterpretation but the texture is tiled."); "reinterpretation but the texture is tiled.");
} }
size_t remaining_size = new_params.SizeInBytes() - params.SizeInBytes(); size_t remaining_size = new_params.SizeInBytes() - params.SizeInBytes();
@ -859,21 +874,38 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
const auto& dest_rect{new_params.GetRect()}; const auto& dest_rect{new_params.GetRect()};
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo.handle); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, copy_pbo.handle);
if (dest_format.compressed) { if (dest_format.compressed) {
glCompressedTexSubImage2D( LOG_CRITICAL(HW_GPU, "Compressed copy is unimplemented!");
GL_TEXTURE_2D, 0, 0, 0, static_cast<GLsizei>(dest_rect.GetWidth()), UNREACHABLE();
static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format,
static_cast<GLsizei>(new_params.SizeInBytes()), nullptr);
} else { } 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, glTextureSubImage2D(new_surface->Texture().handle, 0, 0, 0,
static_cast<GLsizei>(dest_rect.GetWidth()), static_cast<GLsizei>(dest_rect.GetWidth()),
static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format, static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format,
dest_format.type, nullptr); 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();
}
} }
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
pbo.Release();
} }
return new_surface; return new_surface;

View File

@ -109,6 +109,33 @@ struct SurfaceParams {
Invalid = 4, Invalid = 4,
}; };
enum class SurfaceTarget {
Texture1D,
Texture2D,
Texture3D,
Texture1DArray,
Texture2DArray,
TextureCubemap,
};
static SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type) {
switch (texture_type) {
case Tegra::Texture::TextureType::Texture1D:
return SurfaceTarget::Texture1D;
case Tegra::Texture::TextureType::Texture2D:
case Tegra::Texture::TextureType::Texture2DNoMipmap:
return SurfaceTarget::Texture2D;
case Tegra::Texture::TextureType::Texture1DArray:
return SurfaceTarget::Texture1DArray;
case Tegra::Texture::TextureType::Texture2DArray:
return SurfaceTarget::Texture2DArray;
default:
LOG_CRITICAL(HW_GPU, "Unimplemented texture_type={}", static_cast<u32>(texture_type));
UNREACHABLE();
return SurfaceTarget::Texture2D;
}
}
/** /**
* Gets the compression factor for the specified PixelFormat. This applies to just the * 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 * "compressed width" and "compressed height", not the overall compression factor of a
@ -635,7 +662,7 @@ struct SurfaceParams {
ASSERT(width % compression_factor == 0); ASSERT(width % compression_factor == 0);
ASSERT(height % compression_factor == 0); ASSERT(height % compression_factor == 0);
return (width / compression_factor) * (height / compression_factor) * return (width / compression_factor) * (height / compression_factor) *
GetFormatBpp(pixel_format) / CHAR_BIT; GetFormatBpp(pixel_format) * depth / CHAR_BIT;
} }
/// Creates SurfaceParams from a texture configuration /// Creates SurfaceParams from a texture configuration
@ -664,8 +691,10 @@ struct SurfaceParams {
SurfaceType type; SurfaceType type;
u32 width; u32 width;
u32 height; u32 height;
u32 depth;
u32 unaligned_height; u32 unaligned_height;
size_t size_in_bytes; size_t size_in_bytes;
SurfaceTarget target;
// Parameters used for caching only // Parameters used for caching only
u32 cache_width; u32 cache_width;
@ -709,6 +738,10 @@ public:
return texture; return texture;
} }
GLenum Target() const {
return gl_target;
}
static constexpr unsigned int GetGLBytesPerPixel(SurfaceParams::PixelFormat format) { static constexpr unsigned int GetGLBytesPerPixel(SurfaceParams::PixelFormat format) {
if (format == SurfaceParams::PixelFormat::Invalid) if (format == SurfaceParams::PixelFormat::Invalid)
return 0; return 0;
@ -724,14 +757,14 @@ public:
void LoadGLBuffer(); void LoadGLBuffer();
void FlushGLBuffer(); void FlushGLBuffer();
// Upload/Download data in gl_buffer in/to this surface's texture // Upload data in gl_buffer to this surface's texture
void UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle); void UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle);
void DownloadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle);
private: private:
OGLTexture texture; OGLTexture texture;
std::vector<u8> gl_buffer; std::vector<u8> gl_buffer;
SurfaceParams params; SurfaceParams params;
GLenum gl_target;
}; };
class RasterizerCacheOpenGL final : public RasterizerCache<Surface> { class RasterizerCacheOpenGL final : public RasterizerCache<Surface> {
@ -774,6 +807,10 @@ private:
OGLFramebuffer read_framebuffer; OGLFramebuffer read_framebuffer;
OGLFramebuffer draw_framebuffer; OGLFramebuffer draw_framebuffer;
/// Use a Pixel Buffer Object to download the previous texture and then upload it to the new one
/// using the new format.
OGLBuffer copy_pbo;
}; };
} // namespace OpenGL } // namespace OpenGL

View File

@ -443,13 +443,12 @@ public:
} }
declarations.AddNewLine(); declarations.AddNewLine();
// Append the sampler2D array for the used textures. const auto& samplers = GetSamplers();
const size_t num_samplers = used_samplers.size(); for (const auto& sampler : samplers) {
if (num_samplers > 0) { declarations.AddLine("uniform " + sampler.GetTypeString() + ' ' + sampler.GetName() +
declarations.AddLine("uniform sampler2D " + SamplerEntry::GetArrayName(stage) + '[' + ';');
std::to_string(num_samplers) + "];");
declarations.AddNewLine();
} }
declarations.AddNewLine();
} }
/// Returns a list of constant buffer declarations /// Returns a list of constant buffer declarations
@ -461,13 +460,14 @@ public:
} }
/// Returns a list of samplers used in the shader /// Returns a list of samplers used in the shader
std::vector<SamplerEntry> GetSamplers() const { const std::vector<SamplerEntry>& GetSamplers() const {
return used_samplers; return used_samplers;
} }
/// Returns the GLSL sampler used for the input shader sampler, and creates a new one if /// Returns the GLSL sampler used for the input shader sampler, and creates a new one if
/// necessary. /// necessary.
std::string AccessSampler(const Sampler& sampler) { std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type,
bool is_array) {
size_t offset = static_cast<size_t>(sampler.index.Value()); size_t offset = static_cast<size_t>(sampler.index.Value());
// If this sampler has already been used, return the existing mapping. // If this sampler has already been used, return the existing mapping.
@ -476,12 +476,13 @@ public:
[&](const SamplerEntry& entry) { return entry.GetOffset() == offset; }); [&](const SamplerEntry& entry) { return entry.GetOffset() == offset; });
if (itr != used_samplers.end()) { if (itr != used_samplers.end()) {
ASSERT(itr->GetType() == type && itr->IsArray() == is_array);
return itr->GetName(); return itr->GetName();
} }
// Otherwise create a new mapping for this sampler // Otherwise create a new mapping for this sampler
size_t next_index = used_samplers.size(); size_t next_index = used_samplers.size();
SamplerEntry entry{stage, offset, next_index}; SamplerEntry entry{stage, offset, next_index, type, is_array};
used_samplers.emplace_back(entry); used_samplers.emplace_back(entry);
return entry.GetName(); return entry.GetName();
} }
@ -722,8 +723,8 @@ private:
} }
/// Generates code representing a texture sampler. /// Generates code representing a texture sampler.
std::string GetSampler(const Sampler& sampler) { std::string GetSampler(const Sampler& sampler, Tegra::Shader::TextureType type, bool is_array) {
return regs.AccessSampler(sampler); return regs.AccessSampler(sampler, type, is_array);
} }
/** /**
@ -1753,10 +1754,35 @@ private:
break; break;
} }
case OpCode::Id::TEX: { case OpCode::Id::TEX: {
const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); ASSERT_MSG(instr.tex.array == 0, "TEX arrays unimplemented");
const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); Tegra::Shader::TextureType texture_type{instr.tex.texture_type};
const std::string sampler = GetSampler(instr.sampler); std::string coord;
const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
switch (texture_type) {
case Tegra::Shader::TextureType::Texture1D: {
std::string x = regs.GetRegisterAsFloat(instr.gpr8);
coord = "float coords = " + x + ';';
break;
}
case Tegra::Shader::TextureType::Texture2D: {
std::string x = regs.GetRegisterAsFloat(instr.gpr8);
std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
coord = "vec2 coords = vec2(" + x + ", " + y + ");";
break;
}
default:
LOG_CRITICAL(HW_GPU, "Unhandled texture type {}",
static_cast<u32>(texture_type));
UNREACHABLE();
// Fallback to interpreting as a 2D texture for now
std::string x = regs.GetRegisterAsFloat(instr.gpr8);
std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
coord = "vec2 coords = vec2(" + x + ", " + y + ");";
texture_type = Tegra::Shader::TextureType::Texture2D;
}
const std::string sampler = GetSampler(instr.sampler, texture_type, false);
// Add an extra scope and declare the texture coords inside to prevent // Add an extra scope and declare the texture coords inside to prevent
// overwriting them in case they are used as outputs of the texs instruction. // overwriting them in case they are used as outputs of the texs instruction.
shader.AddLine("{"); shader.AddLine("{");
@ -1778,20 +1804,65 @@ private:
break; break;
} }
case OpCode::Id::TEXS: { case OpCode::Id::TEXS: {
const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); std::string coord;
const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20); Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()};
const std::string sampler = GetSampler(instr.sampler); bool is_array{instr.texs.IsArrayTexture()};
const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
switch (texture_type) {
case Tegra::Shader::TextureType::Texture2D: {
if (is_array) {
std::string index = regs.GetRegisterAsInteger(instr.gpr8);
std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
std::string y = regs.GetRegisterAsFloat(instr.gpr20);
coord = "vec3 coords = vec3(" + x + ", " + y + ", " + index + ");";
} else {
std::string x = regs.GetRegisterAsFloat(instr.gpr8);
std::string y = regs.GetRegisterAsFloat(instr.gpr20);
coord = "vec2 coords = vec2(" + x + ", " + y + ");";
}
break;
}
default:
LOG_CRITICAL(HW_GPU, "Unhandled texture type {}",
static_cast<u32>(texture_type));
UNREACHABLE();
// Fallback to interpreting as a 2D texture for now
std::string x = regs.GetRegisterAsFloat(instr.gpr8);
std::string y = regs.GetRegisterAsFloat(instr.gpr20);
coord = "vec2 coords = vec2(" + x + ", " + y + ");";
texture_type = Tegra::Shader::TextureType::Texture2D;
is_array = false;
}
const std::string sampler = GetSampler(instr.sampler, texture_type, is_array);
const std::string texture = "texture(" + sampler + ", coords)"; const std::string texture = "texture(" + sampler + ", coords)";
WriteTexsInstruction(instr, coord, texture); WriteTexsInstruction(instr, coord, texture);
break; break;
} }
case OpCode::Id::TLDS: { case OpCode::Id::TLDS: {
const std::string op_a = regs.GetRegisterAsInteger(instr.gpr8); ASSERT(instr.tlds.GetTextureType() == Tegra::Shader::TextureType::Texture2D);
const std::string op_b = regs.GetRegisterAsInteger(instr.gpr20); ASSERT(instr.tlds.IsArrayTexture() == false);
const std::string sampler = GetSampler(instr.sampler); std::string coord;
const std::string coord = "ivec2 coords = ivec2(" + op_a + ", " + op_b + ");";
switch (instr.tlds.GetTextureType()) {
case Tegra::Shader::TextureType::Texture2D: {
if (instr.tlds.IsArrayTexture()) {
LOG_CRITICAL(HW_GPU, "Unhandled 2d array texture");
UNREACHABLE();
} else {
std::string x = regs.GetRegisterAsInteger(instr.gpr8);
std::string y = regs.GetRegisterAsInteger(instr.gpr20);
coord = "ivec2 coords = ivec2(" + x + ", " + y + ");";
}
break;
}
default:
LOG_CRITICAL(HW_GPU, "Unhandled texture type {}",
static_cast<u32>(instr.tlds.GetTextureType()));
UNREACHABLE();
}
const std::string sampler = GetSampler(instr.sampler, instr.tlds.GetTextureType(),
instr.tlds.IsArrayTexture());
const std::string texture = "texelFetch(" + sampler + ", coords, 0)"; const std::string texture = "texelFetch(" + sampler + ", coords, 0)";
WriteTexsInstruction(instr, coord, texture); WriteTexsInstruction(instr, coord, texture);
break; break;
@ -1799,7 +1870,7 @@ private:
case OpCode::Id::TLD4: { case OpCode::Id::TLD4: {
ASSERT(instr.tld4.texture_type == Tegra::Shader::TextureType::Texture2D); ASSERT(instr.tld4.texture_type == Tegra::Shader::TextureType::Texture2D);
ASSERT(instr.tld4.array == 0); ASSERT(instr.tld4.array == 0);
std::string coord{}; std::string coord;
switch (instr.tld4.texture_type) { switch (instr.tld4.texture_type) {
case Tegra::Shader::TextureType::Texture2D: { case Tegra::Shader::TextureType::Texture2D: {
@ -1814,7 +1885,8 @@ private:
UNREACHABLE(); UNREACHABLE();
} }
const std::string sampler = GetSampler(instr.sampler); const std::string sampler =
GetSampler(instr.sampler, instr.tld4.texture_type, false);
// Add an extra scope and declare the texture coords inside to prevent // Add an extra scope and declare the texture coords inside to prevent
// overwriting them in case they are used as outputs of the texs instruction. // overwriting them in case they are used as outputs of the texs instruction.
shader.AddLine("{"); shader.AddLine("{");
@ -1840,7 +1912,8 @@ private:
const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20); const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20);
// TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction. // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction.
const std::string sampler = GetSampler(instr.sampler); const std::string sampler =
GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false);
const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");"; const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
const std::string texture = "textureGather(" + sampler + ", coords, " + const std::string texture = "textureGather(" + sampler + ", coords, " +
std::to_string(instr.tld4s.component) + ')'; std::to_string(instr.tld4s.component) + ')';

View File

@ -9,6 +9,7 @@
#include <vector> #include <vector>
#include "common/common_types.h" #include "common/common_types.h"
#include "video_core/engines/shader_bytecode.h"
namespace OpenGL::GLShader { namespace OpenGL::GLShader {
@ -73,8 +74,9 @@ class SamplerEntry {
using Maxwell = Tegra::Engines::Maxwell3D::Regs; using Maxwell = Tegra::Engines::Maxwell3D::Regs;
public: public:
SamplerEntry(Maxwell::ShaderStage stage, size_t offset, size_t index) SamplerEntry(Maxwell::ShaderStage stage, size_t offset, size_t index,
: offset(offset), stage(stage), sampler_index(index) {} Tegra::Shader::TextureType type, bool is_array)
: offset(offset), stage(stage), sampler_index(index), type(type), is_array(is_array) {}
size_t GetOffset() const { size_t GetOffset() const {
return offset; return offset;
@ -89,8 +91,41 @@ public:
} }
std::string GetName() const { std::string GetName() const {
return std::string(TextureSamplerNames[static_cast<size_t>(stage)]) + '[' + return std::string(TextureSamplerNames[static_cast<size_t>(stage)]) + '_' +
std::to_string(sampler_index) + ']'; std::to_string(sampler_index);
}
std::string GetTypeString() const {
using Tegra::Shader::TextureType;
std::string glsl_type;
switch (type) {
case TextureType::Texture1D:
glsl_type = "sampler1D";
break;
case TextureType::Texture2D:
glsl_type = "sampler2D";
break;
case TextureType::Texture3D:
glsl_type = "sampler3D";
break;
case TextureType::TextureCube:
glsl_type = "samplerCube";
break;
default:
UNIMPLEMENTED();
}
if (is_array)
glsl_type += "Array";
return glsl_type;
}
Tegra::Shader::TextureType GetType() const {
return type;
}
bool IsArray() const {
return is_array;
} }
u32 GetHash() const { u32 GetHash() const {
@ -105,11 +140,14 @@ private:
static constexpr std::array<const char*, Maxwell::MaxShaderStage> TextureSamplerNames = { static constexpr std::array<const char*, Maxwell::MaxShaderStage> TextureSamplerNames = {
"tex_vs", "tex_tessc", "tex_tesse", "tex_gs", "tex_fs", "tex_vs", "tex_tessc", "tex_tesse", "tex_gs", "tex_fs",
}; };
/// Offset in TSC memory from which to read the sampler object, as specified by the sampling /// Offset in TSC memory from which to read the sampler object, as specified by the sampling
/// instruction. /// instruction.
size_t offset; size_t offset;
Maxwell::ShaderStage stage; ///< Shader stage where this sampler was used. Maxwell::ShaderStage stage; ///< Shader stage where this sampler was used.
size_t sampler_index; ///< Value used to index into the generated GLSL sampler array. size_t sampler_index; ///< Value used to index into the generated GLSL sampler array.
Tegra::Shader::TextureType type; ///< The type used to sample this texture (Texture2D, etc)
bool is_array; ///< Whether the texture is being sampled as an array texture or not.
}; };
struct ShaderEntries { struct ShaderEntries {

View File

@ -200,9 +200,9 @@ void OpenGLState::Apply() const {
const auto& texture_unit = texture_units[i]; const auto& texture_unit = texture_units[i];
const auto& cur_state_texture_unit = cur_state.texture_units[i]; const auto& cur_state_texture_unit = cur_state.texture_units[i];
if (texture_unit.texture_2d != cur_state_texture_unit.texture_2d) { if (texture_unit.texture != cur_state_texture_unit.texture) {
glActiveTexture(TextureUnits::MaxwellTexture(static_cast<int>(i)).Enum()); glActiveTexture(TextureUnits::MaxwellTexture(static_cast<int>(i)).Enum());
glBindTexture(GL_TEXTURE_2D, texture_unit.texture_2d); glBindTexture(texture_unit.target, texture_unit.texture);
} }
if (texture_unit.sampler != cur_state_texture_unit.sampler) { if (texture_unit.sampler != cur_state_texture_unit.sampler) {
glBindSampler(static_cast<GLuint>(i), texture_unit.sampler); glBindSampler(static_cast<GLuint>(i), texture_unit.sampler);
@ -214,7 +214,7 @@ void OpenGLState::Apply() const {
texture_unit.swizzle.a != cur_state_texture_unit.swizzle.a) { texture_unit.swizzle.a != cur_state_texture_unit.swizzle.a) {
std::array<GLint, 4> mask = {texture_unit.swizzle.r, texture_unit.swizzle.g, std::array<GLint, 4> mask = {texture_unit.swizzle.r, texture_unit.swizzle.g,
texture_unit.swizzle.b, texture_unit.swizzle.a}; texture_unit.swizzle.b, texture_unit.swizzle.a};
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, mask.data()); glTexParameteriv(texture_unit.target, GL_TEXTURE_SWIZZLE_RGBA, mask.data());
} }
} }
@ -287,7 +287,7 @@ void OpenGLState::Apply() const {
OpenGLState& OpenGLState::UnbindTexture(GLuint handle) { OpenGLState& OpenGLState::UnbindTexture(GLuint handle) {
for (auto& unit : texture_units) { for (auto& unit : texture_units) {
if (unit.texture_2d == handle) { if (unit.texture == handle) {
unit.Unbind(); unit.Unbind();
} }
} }

View File

@ -94,8 +94,9 @@ public:
// 3 texture units - one for each that is used in PICA fragment shader emulation // 3 texture units - one for each that is used in PICA fragment shader emulation
struct TextureUnit { struct TextureUnit {
GLuint texture_2d; // GL_TEXTURE_BINDING_2D GLuint texture; // GL_TEXTURE_BINDING_2D
GLuint sampler; // GL_SAMPLER_BINDING GLuint sampler; // GL_SAMPLER_BINDING
GLenum target;
struct { struct {
GLint r; // GL_TEXTURE_SWIZZLE_R GLint r; // GL_TEXTURE_SWIZZLE_R
GLint g; // GL_TEXTURE_SWIZZLE_G GLint g; // GL_TEXTURE_SWIZZLE_G
@ -104,7 +105,7 @@ public:
} swizzle; } swizzle;
void Unbind() { void Unbind() {
texture_2d = 0; texture = 0;
swizzle.r = GL_RED; swizzle.r = GL_RED;
swizzle.g = GL_GREEN; swizzle.g = GL_GREEN;
swizzle.b = GL_BLUE; swizzle.b = GL_BLUE;
@ -114,6 +115,7 @@ public:
void Reset() { void Reset() {
Unbind(); Unbind();
sampler = 0; sampler = 0;
target = GL_TEXTURE_2D;
} }
}; };
std::array<TextureUnit, 32> texture_units; std::array<TextureUnit, 32> texture_units;

View File

@ -177,7 +177,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
Memory::GetPointer(framebuffer_addr), Memory::GetPointer(framebuffer_addr),
gl_framebuffer_data.data(), true); gl_framebuffer_data.data(), true);
state.texture_units[0].texture_2d = screen_info.texture.resource.handle; state.texture_units[0].texture = screen_info.texture.resource.handle;
state.Apply(); state.Apply();
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
@ -194,7 +194,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
state.texture_units[0].texture_2d = 0; state.texture_units[0].texture = 0;
state.Apply(); state.Apply();
} }
} }
@ -205,7 +205,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
*/ */
void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a,
const TextureInfo& texture) { const TextureInfo& texture) {
state.texture_units[0].texture_2d = texture.resource.handle; state.texture_units[0].texture = texture.resource.handle;
state.Apply(); state.Apply();
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
@ -214,7 +214,7 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
// Update existing texture // Update existing texture
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer_data); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer_data);
state.texture_units[0].texture_2d = 0; state.texture_units[0].texture = 0;
state.Apply(); state.Apply();
} }
@ -260,7 +260,7 @@ void RendererOpenGL::InitOpenGLObjects() {
// Allocation of storage is deferred until the first frame, when we // Allocation of storage is deferred until the first frame, when we
// know the framebuffer size. // know the framebuffer size.
state.texture_units[0].texture_2d = screen_info.texture.resource.handle; state.texture_units[0].texture = screen_info.texture.resource.handle;
state.Apply(); state.Apply();
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
@ -272,7 +272,7 @@ void RendererOpenGL::InitOpenGLObjects() {
screen_info.display_texture = screen_info.texture.resource.handle; screen_info.display_texture = screen_info.texture.resource.handle;
state.texture_units[0].texture_2d = 0; state.texture_units[0].texture = 0;
state.Apply(); state.Apply();
// Clear screen to black // Clear screen to black
@ -305,14 +305,14 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
UNREACHABLE(); UNREACHABLE();
} }
state.texture_units[0].texture_2d = texture.resource.handle; state.texture_units[0].texture = texture.resource.handle;
state.Apply(); state.Apply();
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0, glTexImage2D(GL_TEXTURE_2D, 0, internal_format, texture.width, texture.height, 0,
texture.gl_format, texture.gl_type, nullptr); texture.gl_format, texture.gl_type, nullptr);
state.texture_units[0].texture_2d = 0; state.texture_units[0].texture = 0;
state.Apply(); state.Apply();
} }
@ -354,14 +354,14 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, right * scale_v), ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, right * scale_v),
}}; }};
state.texture_units[0].texture_2d = screen_info.display_texture; state.texture_units[0].texture = screen_info.display_texture;
state.texture_units[0].swizzle = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA}; state.texture_units[0].swizzle = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA};
state.Apply(); state.Apply();
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data()); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices.data());
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
state.texture_units[0].texture_2d = 0; state.texture_units[0].texture = 0;
state.Apply(); state.Apply();
} }

View File

@ -170,8 +170,12 @@ struct TICEntry {
BitField<0, 16, u32> width_minus_1; BitField<0, 16, u32> width_minus_1;
BitField<23, 4, TextureType> texture_type; BitField<23, 4, TextureType> texture_type;
}; };
u16 height_minus_1; union {
INSERT_PADDING_BYTES(10); BitField<0, 16, u32> height_minus_1;
BitField<16, 15, u32> depth_minus_1;
};
INSERT_PADDING_BYTES(8);
GPUVAddr Address() const { GPUVAddr Address() const {
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low); return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | address_low);
@ -192,6 +196,10 @@ struct TICEntry {
return height_minus_1 + 1; return height_minus_1 + 1;
} }
u32 Depth() const {
return depth_minus_1 + 1;
}
u32 BlockHeight() const { u32 BlockHeight() const {
ASSERT(header_version == TICHeaderVersion::BlockLinear || ASSERT(header_version == TICHeaderVersion::BlockLinear ||
header_version == TICHeaderVersion::BlockLinearColorKey); header_version == TICHeaderVersion::BlockLinearColorKey);