Merge pull request #2759 from ReinUsesLisp/compute-images

gl_rasterizer: Bind images and samplers to compute
master
Fernando Sahmkow 2019-09-10 08:57:05 +07:00 committed by GitHub
commit 434d0922dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 466 additions and 238 deletions

@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version // Licensed under GPLv2 or any later version
// Refer to the license.txt file included. // Refer to the license.txt file included.
#include <bitset>
#include "common/assert.h" #include "common/assert.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/core.h" #include "core/core.h"
@ -49,6 +50,33 @@ void KeplerCompute::CallMethod(const GPU::MethodCall& method_call) {
} }
} }
Tegra::Texture::FullTextureInfo KeplerCompute::GetTexture(std::size_t offset) const {
const std::bitset<8> cbuf_mask = launch_description.const_buffer_enable_mask.Value();
ASSERT(cbuf_mask[regs.tex_cb_index]);
const auto& texinfo = launch_description.const_buffer_config[regs.tex_cb_index];
ASSERT(texinfo.Address() != 0);
const GPUVAddr address = texinfo.Address() + offset * sizeof(Texture::TextureHandle);
ASSERT(address < texinfo.Address() + texinfo.size);
const Texture::TextureHandle tex_handle{memory_manager.Read<u32>(address)};
return GetTextureInfo(tex_handle, offset);
}
Texture::FullTextureInfo KeplerCompute::GetTextureInfo(const Texture::TextureHandle tex_handle,
std::size_t offset) const {
return Texture::FullTextureInfo{static_cast<u32>(offset), GetTICEntry(tex_handle.tic_id),
GetTSCEntry(tex_handle.tsc_id)};
}
u32 KeplerCompute::AccessConstBuffer32(u64 const_buffer, u64 offset) const {
const auto& buffer = launch_description.const_buffer_config[const_buffer];
u32 result;
std::memcpy(&result, memory_manager.GetPointer(buffer.Address() + offset), sizeof(u32));
return result;
}
void KeplerCompute::ProcessLaunch() { void KeplerCompute::ProcessLaunch() {
const GPUVAddr launch_desc_loc = regs.launch_desc_loc.Address(); const GPUVAddr launch_desc_loc = regs.launch_desc_loc.Address();
memory_manager.ReadBlockUnsafe(launch_desc_loc, &launch_description, memory_manager.ReadBlockUnsafe(launch_desc_loc, &launch_description,
@ -60,4 +88,29 @@ void KeplerCompute::ProcessLaunch() {
rasterizer.DispatchCompute(code_addr); rasterizer.DispatchCompute(code_addr);
} }
Texture::TICEntry KeplerCompute::GetTICEntry(u32 tic_index) const {
const GPUVAddr tic_address_gpu{regs.tic.Address() + tic_index * sizeof(Texture::TICEntry)};
Texture::TICEntry tic_entry;
memory_manager.ReadBlockUnsafe(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry));
const auto r_type{tic_entry.r_type.Value()};
const auto g_type{tic_entry.g_type.Value()};
const auto b_type{tic_entry.b_type.Value()};
const auto a_type{tic_entry.a_type.Value()};
// TODO(Subv): Different data types for separate components are not supported
DEBUG_ASSERT(r_type == g_type && r_type == b_type && r_type == a_type);
return tic_entry;
}
Texture::TSCEntry KeplerCompute::GetTSCEntry(u32 tsc_index) const {
const GPUVAddr tsc_address_gpu{regs.tsc.Address() + tsc_index * sizeof(Texture::TSCEntry)};
Texture::TSCEntry tsc_entry;
memory_manager.ReadBlockUnsafe(tsc_address_gpu, &tsc_entry, sizeof(Texture::TSCEntry));
return tsc_entry;
}
} // namespace Tegra::Engines } // namespace Tegra::Engines

@ -12,6 +12,7 @@
#include "common/common_types.h" #include "common/common_types.h"
#include "video_core/engines/engine_upload.h" #include "video_core/engines/engine_upload.h"
#include "video_core/gpu.h" #include "video_core/gpu.h"
#include "video_core/textures/texture.h"
namespace Core { namespace Core {
class System; class System;
@ -111,7 +112,7 @@ public:
INSERT_PADDING_WORDS(0x3FE); INSERT_PADDING_WORDS(0x3FE);
u32 texture_const_buffer_index; u32 tex_cb_index;
INSERT_PADDING_WORDS(0x374); INSERT_PADDING_WORDS(0x374);
}; };
@ -149,7 +150,7 @@ public:
union { union {
BitField<0, 8, u32> const_buffer_enable_mask; BitField<0, 8, u32> const_buffer_enable_mask;
BitField<29, 2, u32> cache_layout; BitField<29, 2, u32> cache_layout;
} memory_config; };
INSERT_PADDING_WORDS(0x8); INSERT_PADDING_WORDS(0x8);
@ -194,6 +195,14 @@ public:
/// Write the value to the register identified by method. /// Write the value to the register identified by method.
void CallMethod(const GPU::MethodCall& method_call); void CallMethod(const GPU::MethodCall& method_call);
Tegra::Texture::FullTextureInfo GetTexture(std::size_t offset) const;
/// Given a Texture Handle, returns the TSC and TIC entries.
Texture::FullTextureInfo GetTextureInfo(const Texture::TextureHandle tex_handle,
std::size_t offset) const;
u32 AccessConstBuffer32(u64 const_buffer, u64 offset) const;
private: private:
Core::System& system; Core::System& system;
VideoCore::RasterizerInterface& rasterizer; VideoCore::RasterizerInterface& rasterizer;
@ -201,6 +210,12 @@ private:
Upload::State upload_state; Upload::State upload_state;
void ProcessLaunch(); void ProcessLaunch();
/// Retrieves information about a specific TIC entry from the TIC buffer.
Texture::TICEntry GetTICEntry(u32 tic_index) const;
/// Retrieves information about a specific TSC entry from the TSC buffer.
Texture::TSCEntry GetTSCEntry(u32 tsc_index) const;
}; };
#define ASSERT_REG_POSITION(field_name, position) \ #define ASSERT_REG_POSITION(field_name, position) \
@ -218,12 +233,12 @@ ASSERT_REG_POSITION(launch, 0xAF);
ASSERT_REG_POSITION(tsc, 0x557); ASSERT_REG_POSITION(tsc, 0x557);
ASSERT_REG_POSITION(tic, 0x55D); ASSERT_REG_POSITION(tic, 0x55D);
ASSERT_REG_POSITION(code_loc, 0x582); ASSERT_REG_POSITION(code_loc, 0x582);
ASSERT_REG_POSITION(texture_const_buffer_index, 0x982); ASSERT_REG_POSITION(tex_cb_index, 0x982);
ASSERT_LAUNCH_PARAM_POSITION(program_start, 0x8); ASSERT_LAUNCH_PARAM_POSITION(program_start, 0x8);
ASSERT_LAUNCH_PARAM_POSITION(grid_dim_x, 0xC); ASSERT_LAUNCH_PARAM_POSITION(grid_dim_x, 0xC);
ASSERT_LAUNCH_PARAM_POSITION(shared_alloc, 0x11); ASSERT_LAUNCH_PARAM_POSITION(shared_alloc, 0x11);
ASSERT_LAUNCH_PARAM_POSITION(block_dim_x, 0x12); ASSERT_LAUNCH_PARAM_POSITION(block_dim_x, 0x12);
ASSERT_LAUNCH_PARAM_POSITION(memory_config, 0x14); ASSERT_LAUNCH_PARAM_POSITION(const_buffer_enable_mask, 0x14);
ASSERT_LAUNCH_PARAM_POSITION(const_buffer_config, 0x1D); ASSERT_LAUNCH_PARAM_POSITION(const_buffer_config, 0x1D);
#undef ASSERT_REG_POSITION #undef ASSERT_REG_POSITION

@ -62,6 +62,7 @@ public:
static constexpr std::size_t NumVertexAttributes = 32; static constexpr std::size_t NumVertexAttributes = 32;
static constexpr std::size_t NumVaryings = 31; static constexpr std::size_t NumVaryings = 31;
static constexpr std::size_t NumTextureSamplers = 32; static constexpr std::size_t NumTextureSamplers = 32;
static constexpr std::size_t NumImages = 8; // TODO(Rodrigo): Investigate this number
static constexpr std::size_t NumClipDistances = 8; static constexpr std::size_t NumClipDistances = 8;
static constexpr std::size_t MaxShaderProgram = 6; static constexpr std::size_t MaxShaderProgram = 6;
static constexpr std::size_t MaxShaderStage = 5; static constexpr std::size_t MaxShaderStage = 5;

@ -331,7 +331,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
const auto stage_enum = static_cast<Maxwell::ShaderStage>(stage); const auto stage_enum = static_cast<Maxwell::ShaderStage>(stage);
SetupDrawConstBuffers(stage_enum, shader); SetupDrawConstBuffers(stage_enum, shader);
SetupDrawGlobalMemory(stage_enum, shader); SetupDrawGlobalMemory(stage_enum, shader);
const auto texture_buffer_usage{SetupTextures(stage_enum, shader, base_bindings)}; const auto texture_buffer_usage{SetupDrawTextures(stage_enum, shader, base_bindings)};
const ProgramVariant variant{base_bindings, primitive_mode, texture_buffer_usage}; const ProgramVariant variant{base_bindings, primitive_mode, texture_buffer_usage};
const auto [program_handle, next_bindings] = shader->GetProgramHandle(variant); const auto [program_handle, next_bindings] = shader->GetProgramHandle(variant);
@ -801,7 +801,11 @@ void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
} }
auto kernel = shader_cache.GetComputeKernel(code_addr); auto kernel = shader_cache.GetComputeKernel(code_addr);
const auto [program, next_bindings] = kernel->GetProgramHandle({}); ProgramVariant variant;
variant.texture_buffer_usage = SetupComputeTextures(kernel);
SetupComputeImages(kernel);
const auto [program, next_bindings] = kernel->GetProgramHandle(variant);
state.draw.shader_program = program; state.draw.shader_program = program;
state.draw.program_pipeline = 0; state.draw.program_pipeline = 0;
@ -816,13 +820,13 @@ void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
SetupComputeConstBuffers(kernel); SetupComputeConstBuffers(kernel);
SetupComputeGlobalMemory(kernel); SetupComputeGlobalMemory(kernel);
// TODO(Rodrigo): Bind images and samplers
buffer_cache.Unmap(); buffer_cache.Unmap();
bind_ubo_pushbuffer.Bind(); bind_ubo_pushbuffer.Bind();
bind_ssbo_pushbuffer.Bind(); bind_ssbo_pushbuffer.Bind();
state.ApplyTextures();
state.ApplyImages();
state.ApplyShaderProgram(); state.ApplyShaderProgram();
state.ApplyProgramPipeline(); state.ApplyProgramPipeline();
@ -922,7 +926,7 @@ void RasterizerOpenGL::SetupComputeConstBuffers(const Shader& kernel) {
const auto& launch_desc = system.GPU().KeplerCompute().launch_description; const auto& launch_desc = system.GPU().KeplerCompute().launch_description;
for (const auto& entry : kernel->GetShaderEntries().const_buffers) { for (const auto& entry : kernel->GetShaderEntries().const_buffers) {
const auto& config = launch_desc.const_buffer_config[entry.GetIndex()]; const auto& config = launch_desc.const_buffer_config[entry.GetIndex()];
const std::bitset<8> mask = launch_desc.memory_config.const_buffer_enable_mask.Value(); const std::bitset<8> mask = launch_desc.const_buffer_enable_mask.Value();
Tegra::Engines::ConstBufferInfo buffer; Tegra::Engines::ConstBufferInfo buffer;
buffer.address = config.Address(); buffer.address = config.Address();
buffer.size = config.size; buffer.size = config.size;
@ -981,53 +985,125 @@ void RasterizerOpenGL::SetupGlobalMemory(const GLShader::GlobalMemoryEntry& entr
bind_ssbo_pushbuffer.Push(ssbo, buffer_offset, static_cast<GLsizeiptr>(size)); bind_ssbo_pushbuffer.Push(ssbo, buffer_offset, static_cast<GLsizeiptr>(size));
} }
TextureBufferUsage RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& shader, TextureBufferUsage RasterizerOpenGL::SetupDrawTextures(Maxwell::ShaderStage stage,
const Shader& shader,
BaseBindings base_bindings) { BaseBindings base_bindings) {
MICROPROFILE_SCOPE(OpenGL_Texture); MICROPROFILE_SCOPE(OpenGL_Texture);
const auto& gpu = system.GPU(); const auto& gpu = system.GPU();
const auto& maxwell3d = gpu.Maxwell3D(); const auto& maxwell3d = gpu.Maxwell3D();
const auto& entries = shader->GetShaderEntries().samplers; const auto& entries = shader->GetShaderEntries().samplers;
ASSERT_MSG(base_bindings.sampler + entries.size() <= std::size(state.texture_units), ASSERT_MSG(base_bindings.sampler + entries.size() <= std::size(state.textures),
"Exceeded the number of active textures."); "Exceeded the number of active textures.");
TextureBufferUsage texture_buffer_usage{0}; TextureBufferUsage texture_buffer_usage{0};
for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
const auto& entry = entries[bindpoint]; const auto& entry = entries[bindpoint];
Tegra::Texture::FullTextureInfo texture; const auto texture = [&]() {
if (entry.IsBindless()) { if (!entry.IsBindless()) {
return maxwell3d.GetStageTexture(stage, entry.GetOffset());
}
const auto cbuf = entry.GetBindlessCBuf(); const auto cbuf = entry.GetBindlessCBuf();
Tegra::Texture::TextureHandle tex_handle; Tegra::Texture::TextureHandle tex_handle;
tex_handle.raw = maxwell3d.AccessConstBuffer32(stage, cbuf.first, cbuf.second); tex_handle.raw = maxwell3d.AccessConstBuffer32(stage, cbuf.first, cbuf.second);
texture = maxwell3d.GetTextureInfo(tex_handle, entry.GetOffset()); return maxwell3d.GetTextureInfo(tex_handle, entry.GetOffset());
} else { }();
texture = maxwell3d.GetStageTexture(stage, entry.GetOffset());
}
const u32 current_bindpoint = base_bindings.sampler + bindpoint;
auto& unit{state.texture_units[current_bindpoint]}; if (SetupTexture(base_bindings.sampler + bindpoint, texture, entry)) {
unit.sampler = sampler_cache.GetSampler(texture.tsc);
if (const auto view{texture_cache.GetTextureSurface(texture, entry)}; view) {
if (view->GetSurfaceParams().IsBuffer()) {
// Record that this texture is a texture buffer.
texture_buffer_usage.set(bindpoint); texture_buffer_usage.set(bindpoint);
} else {
// Apply swizzle to textures that are not buffers.
view->ApplySwizzle(texture.tic.x_source, texture.tic.y_source, texture.tic.z_source,
texture.tic.w_source);
}
state.texture_units[current_bindpoint].texture = view->GetTexture();
} else {
// Can occur when texture addr is null or its memory is unmapped/invalid
unit.texture = 0;
} }
} }
return texture_buffer_usage; return texture_buffer_usage;
} }
TextureBufferUsage RasterizerOpenGL::SetupComputeTextures(const Shader& kernel) {
MICROPROFILE_SCOPE(OpenGL_Texture);
const auto& compute = system.GPU().KeplerCompute();
const auto& entries = kernel->GetShaderEntries().samplers;
ASSERT_MSG(entries.size() <= std::size(state.textures),
"Exceeded the number of active textures.");
TextureBufferUsage texture_buffer_usage{0};
for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
const auto& entry = entries[bindpoint];
const auto texture = [&]() {
if (!entry.IsBindless()) {
return compute.GetTexture(entry.GetOffset());
}
const auto cbuf = entry.GetBindlessCBuf();
Tegra::Texture::TextureHandle tex_handle;
tex_handle.raw = compute.AccessConstBuffer32(cbuf.first, cbuf.second);
return compute.GetTextureInfo(tex_handle, entry.GetOffset());
}();
if (SetupTexture(bindpoint, texture, entry)) {
texture_buffer_usage.set(bindpoint);
}
}
return texture_buffer_usage;
}
bool RasterizerOpenGL::SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
const GLShader::SamplerEntry& entry) {
state.samplers[binding] = sampler_cache.GetSampler(texture.tsc);
const auto view = texture_cache.GetTextureSurface(texture.tic, entry);
if (!view) {
// Can occur when texture addr is null or its memory is unmapped/invalid
state.textures[binding] = 0;
return false;
}
state.textures[binding] = view->GetTexture();
if (view->GetSurfaceParams().IsBuffer()) {
return true;
}
// Apply swizzle to textures that are not buffers.
view->ApplySwizzle(texture.tic.x_source, texture.tic.y_source, texture.tic.z_source,
texture.tic.w_source);
return false;
}
void RasterizerOpenGL::SetupComputeImages(const Shader& shader) {
const auto& compute = system.GPU().KeplerCompute();
const auto& entries = shader->GetShaderEntries().images;
for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
const auto& entry = entries[bindpoint];
const auto tic = [&]() {
if (!entry.IsBindless()) {
return compute.GetTexture(entry.GetOffset()).tic;
}
const auto cbuf = entry.GetBindlessCBuf();
Tegra::Texture::TextureHandle tex_handle;
tex_handle.raw = compute.AccessConstBuffer32(cbuf.first, cbuf.second);
return compute.GetTextureInfo(tex_handle, entry.GetOffset()).tic;
}();
SetupImage(bindpoint, tic, entry);
}
}
void RasterizerOpenGL::SetupImage(u32 binding, const Tegra::Texture::TICEntry& tic,
const GLShader::ImageEntry& entry) {
const auto view = texture_cache.GetImageSurface(tic, entry);
if (!view) {
state.images[binding] = 0;
return;
}
if (!tic.IsBuffer()) {
view->ApplySwizzle(tic.x_source, tic.y_source, tic.z_source, tic.w_source);
}
if (entry.IsWritten()) {
view->MarkAsModified(texture_cache.Tick());
}
state.images[binding] = view->GetTexture();
}
void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) { void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
const auto& regs = system.GPU().Maxwell3D().regs; const auto& regs = system.GPU().Maxwell3D().regs;
const bool geometry_shaders_enabled = const bool geometry_shaders_enabled =

@ -32,6 +32,7 @@
#include "video_core/renderer_opengl/gl_state.h" #include "video_core/renderer_opengl/gl_state.h"
#include "video_core/renderer_opengl/gl_texture_cache.h" #include "video_core/renderer_opengl/gl_texture_cache.h"
#include "video_core/renderer_opengl/utils.h" #include "video_core/renderer_opengl/utils.h"
#include "video_core/textures/texture.h"
namespace Core { namespace Core {
class System; class System;
@ -137,9 +138,23 @@ private:
/// Configures the current textures to use for the draw command. Returns shaders texture buffer /// Configures the current textures to use for the draw command. Returns shaders texture buffer
/// usage. /// usage.
TextureBufferUsage SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, TextureBufferUsage SetupDrawTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
const Shader& shader, BaseBindings base_bindings); const Shader& shader, BaseBindings base_bindings);
/// Configures the textures used in a compute shader. Returns texture buffer usage.
TextureBufferUsage SetupComputeTextures(const Shader& kernel);
/// Configures a texture. Returns true when the texture is a texture buffer.
bool SetupTexture(u32 binding, const Tegra::Texture::FullTextureInfo& texture,
const GLShader::SamplerEntry& entry);
/// Configures images in a compute shader.
void SetupComputeImages(const Shader& shader);
/// Configures an image.
void SetupImage(u32 binding, const Tegra::Texture::TICEntry& tic,
const GLShader::ImageEntry& entry);
/// Syncs the viewport and depth range to match the guest state /// Syncs the viewport and depth range to match the guest state
void SyncViewport(OpenGLState& current_state); void SyncViewport(OpenGLState& current_state);

@ -389,11 +389,10 @@ public:
for (const auto& sampler : ir.GetSamplers()) { for (const auto& sampler : ir.GetSamplers()) {
entries.samplers.emplace_back(sampler); entries.samplers.emplace_back(sampler);
} }
for (const auto& image : ir.GetImages()) { for (const auto& [offset, image] : ir.GetImages()) {
entries.images.emplace_back(image); entries.images.emplace_back(image);
} }
for (const auto& gmem_pair : ir.GetGlobalMemory()) { for (const auto& [base, usage] : ir.GetGlobalMemory()) {
const auto& [base, usage] = gmem_pair;
entries.global_memory_entries.emplace_back(base.cbuf_index, base.cbuf_offset, entries.global_memory_entries.emplace_back(base.cbuf_index, base.cbuf_offset,
usage.is_read, usage.is_written); usage.is_read, usage.is_written);
} }
@ -706,7 +705,7 @@ private:
void DeclareImages() { void DeclareImages() {
const auto& images{ir.GetImages()}; const auto& images{ir.GetImages()};
for (const auto& image : images) { for (const auto& [offset, image] : images) {
const std::string image_type = [&]() { const std::string image_type = [&]() {
switch (image.GetType()) { switch (image.GetType()) {
case Tegra::Shader::ImageType::Texture1D: case Tegra::Shader::ImageType::Texture1D:
@ -726,9 +725,16 @@ private:
return "image1D"; return "image1D";
} }
}(); }();
code.AddLine("layout (binding = IMAGE_BINDING_{}) coherent volatile writeonly uniform " std::string qualifier = "coherent volatile";
if (image.IsRead() && !image.IsWritten()) {
qualifier += " readonly";
} else if (image.IsWritten() && !image.IsRead()) {
qualifier += " writeonly";
}
code.AddLine("layout (binding = IMAGE_BINDING_{}) {} uniform "
"{} {};", "{} {};",
image.GetIndex(), image_type, GetImage(image)); image.GetIndex(), qualifier, image_type, GetImage(image));
} }
if (!images.empty()) { if (!images.empty()) {
code.AddNewLine(); code.AddNewLine();

@ -341,13 +341,16 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
u64 index{}; u64 index{};
u32 type{}; u32 type{};
u8 is_bindless{}; u8 is_bindless{};
u8 is_read{};
u8 is_written{};
if (!LoadObjectFromPrecompiled(offset) || !LoadObjectFromPrecompiled(index) || if (!LoadObjectFromPrecompiled(offset) || !LoadObjectFromPrecompiled(index) ||
!LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_bindless)) { !LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_bindless) ||
!LoadObjectFromPrecompiled(is_read) || !LoadObjectFromPrecompiled(is_written)) {
return {}; return {};
} }
entry.entries.images.emplace_back( entry.entries.images.emplace_back(static_cast<u64>(offset), static_cast<std::size_t>(index),
static_cast<std::size_t>(offset), static_cast<std::size_t>(index), static_cast<Tegra::Shader::ImageType>(type),
static_cast<Tegra::Shader::ImageType>(type), is_bindless != 0); is_bindless != 0, is_written != 0, is_read != 0);
} }
u32 global_memory_count{}; u32 global_memory_count{};
@ -429,7 +432,9 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std:
if (!SaveObjectToPrecompiled(static_cast<u64>(image.GetOffset())) || if (!SaveObjectToPrecompiled(static_cast<u64>(image.GetOffset())) ||
!SaveObjectToPrecompiled(static_cast<u64>(image.GetIndex())) || !SaveObjectToPrecompiled(static_cast<u64>(image.GetIndex())) ||
!SaveObjectToPrecompiled(static_cast<u32>(image.GetType())) || !SaveObjectToPrecompiled(static_cast<u32>(image.GetType())) ||
!SaveObjectToPrecompiled(static_cast<u8>(image.IsBindless() ? 1 : 0))) { !SaveObjectToPrecompiled(static_cast<u8>(image.IsBindless() ? 1 : 0)) ||
!SaveObjectToPrecompiled(static_cast<u8>(image.IsRead() ? 1 : 0)) ||
!SaveObjectToPrecompiled(static_cast<u8>(image.IsWritten() ? 1 : 0))) {
return false; return false;
} }
} }

@ -34,6 +34,25 @@ bool UpdateTie(T1 current_value, const T2 new_value) {
return changed; return changed;
} }
template <typename T>
std::optional<std::pair<GLuint, GLsizei>> UpdateArray(T& current_values, const T& new_values) {
std::optional<std::size_t> first;
std::size_t last;
for (std::size_t i = 0; i < std::size(current_values); ++i) {
if (!UpdateValue(current_values[i], new_values[i])) {
continue;
}
if (!first) {
first = i;
}
last = i;
}
if (!first) {
return std::nullopt;
}
return std::make_pair(static_cast<GLuint>(*first), static_cast<GLsizei>(last - *first + 1));
}
void Enable(GLenum cap, bool enable) { void Enable(GLenum cap, bool enable) {
if (enable) { if (enable) {
glEnable(cap); glEnable(cap);
@ -134,10 +153,6 @@ OpenGLState::OpenGLState() {
logic_op.enabled = false; logic_op.enabled = false;
logic_op.operation = GL_COPY; logic_op.operation = GL_COPY;
for (auto& texture_unit : texture_units) {
texture_unit.Reset();
}
draw.read_framebuffer = 0; draw.read_framebuffer = 0;
draw.draw_framebuffer = 0; draw.draw_framebuffer = 0;
draw.vertex_array = 0; draw.vertex_array = 0;
@ -496,52 +511,20 @@ void OpenGLState::ApplyAlphaTest() const {
} }
void OpenGLState::ApplyTextures() const { void OpenGLState::ApplyTextures() const {
bool has_delta{}; if (const auto update = UpdateArray(cur_state.textures, textures)) {
std::size_t first{}; glBindTextures(update->first, update->second, textures.data() + update->first);
std::size_t last{};
std::array<GLuint, Maxwell::NumTextureSamplers> textures;
for (std::size_t i = 0; i < std::size(texture_units); ++i) {
const auto& texture_unit = texture_units[i];
auto& cur_state_texture_unit = cur_state.texture_units[i];
textures[i] = texture_unit.texture;
if (cur_state_texture_unit.texture == textures[i]) {
continue;
}
cur_state_texture_unit.texture = textures[i];
if (!has_delta) {
first = i;
has_delta = true;
}
last = i;
}
if (has_delta) {
glBindTextures(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1),
textures.data() + first);
} }
} }
void OpenGLState::ApplySamplers() const { void OpenGLState::ApplySamplers() const {
bool has_delta{}; if (const auto update = UpdateArray(cur_state.samplers, samplers)) {
std::size_t first{}; glBindSamplers(update->first, update->second, samplers.data() + update->first);
std::size_t last{}; }
std::array<GLuint, Maxwell::NumTextureSamplers> samplers; }
for (std::size_t i = 0; i < std::size(samplers); ++i) { void OpenGLState::ApplyImages() const {
samplers[i] = texture_units[i].sampler; if (const auto update = UpdateArray(cur_state.images, images)) {
if (cur_state.texture_units[i].sampler == texture_units[i].sampler) { glBindImageTextures(update->first, update->second, images.data() + update->first);
continue;
}
cur_state.texture_units[i].sampler = 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() + first);
} }
} }
@ -576,6 +559,7 @@ void OpenGLState::Apply() {
ApplyLogicOp(); ApplyLogicOp();
ApplyTextures(); ApplyTextures();
ApplySamplers(); ApplySamplers();
ApplyImages();
if (dirty.polygon_offset) { if (dirty.polygon_offset) {
ApplyPolygonOffset(); ApplyPolygonOffset();
dirty.polygon_offset = false; dirty.polygon_offset = false;
@ -606,18 +590,18 @@ void OpenGLState::EmulateViewportWithScissor() {
} }
OpenGLState& OpenGLState::UnbindTexture(GLuint handle) { OpenGLState& OpenGLState::UnbindTexture(GLuint handle) {
for (auto& unit : texture_units) { for (auto& texture : textures) {
if (unit.texture == handle) { if (texture == handle) {
unit.Unbind(); texture = 0;
} }
} }
return *this; return *this;
} }
OpenGLState& OpenGLState::ResetSampler(GLuint handle) { OpenGLState& OpenGLState::ResetSampler(GLuint handle) {
for (auto& unit : texture_units) { for (auto& sampler : samplers) {
if (unit.sampler == handle) { if (sampler == handle) {
unit.sampler = 0; sampler = 0;
} }
} }
return *this; return *this;

@ -118,21 +118,9 @@ public:
GLenum operation; GLenum operation;
} logic_op; } logic_op;
// 3 texture units - one for each that is used in PICA fragment shader emulation std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> textures{};
struct TextureUnit { std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> samplers{};
GLuint texture; // GL_TEXTURE_BINDING_2D std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumImages> images{};
GLuint sampler; // GL_SAMPLER_BINDING
void Unbind() {
texture = 0;
}
void Reset() {
Unbind();
sampler = 0;
}
};
std::array<TextureUnit, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> texture_units;
struct { struct {
GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING
@ -220,6 +208,7 @@ public:
void ApplyLogicOp() const; void ApplyLogicOp() const;
void ApplyTextures() const; void ApplyTextures() const;
void ApplySamplers() const; void ApplySamplers() const;
void ApplyImages() const;
void ApplyDepthClamp() const; void ApplyDepthClamp() const;
void ApplyPolygonOffset() const; void ApplyPolygonOffset() const;
void ApplyAlphaTest() const; void ApplyAlphaTest() const;

@ -78,6 +78,17 @@ public:
/// Attaches this texture view to the current bound GL_DRAW_FRAMEBUFFER /// Attaches this texture view to the current bound GL_DRAW_FRAMEBUFFER
void Attach(GLenum attachment, GLenum target) const; void Attach(GLenum attachment, GLenum target) const;
void ApplySwizzle(Tegra::Texture::SwizzleSource x_source,
Tegra::Texture::SwizzleSource y_source,
Tegra::Texture::SwizzleSource z_source,
Tegra::Texture::SwizzleSource w_source);
void DecorateViewName(GPUVAddr gpu_addr, std::string prefix);
void MarkAsModified(u64 tick) {
surface.MarkAsModified(true, tick);
}
GLuint GetTexture() const { GLuint GetTexture() const {
if (is_proxy) { if (is_proxy) {
return surface.GetTexture(); return surface.GetTexture();
@ -89,13 +100,6 @@ public:
return surface.GetSurfaceParams(); return surface.GetSurfaceParams();
} }
void ApplySwizzle(Tegra::Texture::SwizzleSource x_source,
Tegra::Texture::SwizzleSource y_source,
Tegra::Texture::SwizzleSource z_source,
Tegra::Texture::SwizzleSource w_source);
void DecorateViewName(GPUVAddr gpu_addr, std::string prefix);
private: private:
u32 EncodeSwizzle(Tegra::Texture::SwizzleSource x_source, u32 EncodeSwizzle(Tegra::Texture::SwizzleSource x_source,
Tegra::Texture::SwizzleSource y_source, Tegra::Texture::SwizzleSource y_source,
@ -111,8 +115,8 @@ private:
GLenum target{}; GLenum target{};
OGLTextureView texture_view; OGLTextureView texture_view;
u32 swizzle; u32 swizzle{};
bool is_proxy; bool is_proxy{};
}; };
class TextureCacheOpenGL final : public TextureCacheBase { class TextureCacheOpenGL final : public TextureCacheBase {

@ -342,7 +342,7 @@ 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 = screen_info.display_texture; state.textures[0] = screen_info.display_texture;
// Workaround brigthness problems in SMO by enabling sRGB in the final output // Workaround brigthness problems in SMO by enabling sRGB in the final output
// if it has been used in the frame. Needed because of this bug in QT: QTBUG-50987 // if it has been used in the frame. Needed because of this bug in QT: QTBUG-50987
state.framebuffer_srgb.enabled = OpenGLState::GetsRGBUsed(); state.framebuffer_srgb.enabled = OpenGLState::GetsRGBUsed();
@ -352,7 +352,7 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Restore default state // Restore default state
state.framebuffer_srgb.enabled = false; state.framebuffer_srgb.enabled = false;
state.texture_units[0].texture = 0; state.textures[0] = 0;
state.AllDirty(); state.AllDirty();
state.Apply(); state.Apply();
// Clear sRGB state for the next frame // Clear sRGB state for the next frame

@ -61,56 +61,54 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
} }
const auto type{instr.sust.image_type}; const auto type{instr.sust.image_type};
const auto& image{instr.sust.is_immediate ? GetImage(instr.image, type) auto& image{instr.sust.is_immediate ? GetImage(instr.image, type)
: GetBindlessImage(instr.gpr39, type)}; : GetBindlessImage(instr.gpr39, type)};
image.MarkWrite();
MetaImage meta{image, values}; MetaImage meta{image, values};
const Node store{Operation(OperationCode::ImageStore, meta, std::move(coords))}; const Node store{Operation(OperationCode::ImageStore, meta, std::move(coords))};
bb.push_back(store); bb.push_back(store);
break; break;
} }
default: default:
UNIMPLEMENTED_MSG("Unhandled conversion instruction: {}", opcode->get().GetName()); UNIMPLEMENTED_MSG("Unhandled image instruction: {}", opcode->get().GetName());
} }
return pc; return pc;
} }
const Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type) { Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type) {
const auto offset{static_cast<std::size_t>(image.index.Value())}; const auto offset{static_cast<u64>(image.index.Value())};
// If this image has already been used, return the existing mapping. // If this image has already been used, return the existing mapping.
const auto itr{std::find_if(used_images.begin(), used_images.end(), const auto it = used_images.find(offset);
[=](const Image& entry) { return entry.GetOffset() == offset; })}; if (it != used_images.end()) {
if (itr != used_images.end()) { ASSERT(it->second.GetType() == type);
ASSERT(itr->GetType() == type); return it->second;
return *itr;
} }
// Otherwise create a new mapping for this image. // Otherwise create a new mapping for this image.
const std::size_t next_index{used_images.size()}; const std::size_t next_index{used_images.size()};
const Image entry{offset, next_index, type}; return used_images.emplace(offset, Image{offset, next_index, type}).first->second;
return *used_images.emplace(entry).first;
} }
const Image& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg, Image& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type) {
Tegra::Shader::ImageType type) {
const Node image_register{GetRegister(reg)}; const Node image_register{GetRegister(reg)};
const auto [base_image, cbuf_index, cbuf_offset]{ const auto [base_image, cbuf_index, cbuf_offset]{
TrackCbuf(image_register, global_code, static_cast<s64>(global_code.size()))}; TrackCbuf(image_register, global_code, static_cast<s64>(global_code.size()))};
const auto cbuf_key{(static_cast<u64>(cbuf_index) << 32) | static_cast<u64>(cbuf_offset)}; const auto cbuf_key{(static_cast<u64>(cbuf_index) << 32) | static_cast<u64>(cbuf_offset)};
// If this image has already been used, return the existing mapping. // If this image has already been used, return the existing mapping.
const auto itr{std::find_if(used_images.begin(), used_images.end(), const auto it = used_images.find(cbuf_key);
[=](const Image& entry) { return entry.GetOffset() == cbuf_key; })}; if (it != used_images.end()) {
if (itr != used_images.end()) { ASSERT(it->second.GetType() == type);
ASSERT(itr->GetType() == type); return it->second;
return *itr;
} }
// Otherwise create a new mapping for this image. // Otherwise create a new mapping for this image.
const std::size_t next_index{used_images.size()}; const std::size_t next_index{used_images.size()};
const Image entry{cbuf_index, cbuf_offset, next_index, type}; return used_images.emplace(cbuf_key, Image{cbuf_index, cbuf_offset, next_index, type})
return *used_images.emplace(entry).first; .first->second;
} }
} // namespace VideoCommon::Shader } // namespace VideoCommon::Shader

@ -273,46 +273,64 @@ private:
bool is_bindless{}; ///< Whether this sampler belongs to a bindless texture or not. bool is_bindless{}; ///< Whether this sampler belongs to a bindless texture or not.
}; };
class Image { class Image final {
public: public:
explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type) constexpr explicit Image(u64 offset, std::size_t index, Tegra::Shader::ImageType type)
: offset{offset}, index{index}, type{type}, is_bindless{false} {} : offset{offset}, index{index}, type{type}, is_bindless{false} {}
explicit Image(u32 cbuf_index, u32 cbuf_offset, std::size_t index, constexpr explicit Image(u32 cbuf_index, u32 cbuf_offset, std::size_t index,
Tegra::Shader::ImageType type) Tegra::Shader::ImageType type)
: offset{(static_cast<u64>(cbuf_index) << 32) | cbuf_offset}, index{index}, type{type}, : offset{(static_cast<u64>(cbuf_index) << 32) | cbuf_offset}, index{index}, type{type},
is_bindless{true} {} is_bindless{true} {}
explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type, constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type,
bool is_bindless) bool is_bindless, bool is_written, bool is_read)
: offset{offset}, index{index}, type{type}, is_bindless{is_bindless} {} : offset{offset}, index{index}, type{type}, is_bindless{is_bindless},
is_written{is_written}, is_read{is_read} {}
std::size_t GetOffset() const { void MarkRead() {
is_read = true;
}
void MarkWrite() {
is_written = true;
}
constexpr std::size_t GetOffset() const {
return offset; return offset;
} }
std::size_t GetIndex() const { constexpr std::size_t GetIndex() const {
return index; return index;
} }
Tegra::Shader::ImageType GetType() const { constexpr Tegra::Shader::ImageType GetType() const {
return type; return type;
} }
bool IsBindless() const { constexpr bool IsBindless() const {
return is_bindless; return is_bindless;
} }
bool operator<(const Image& rhs) const { constexpr bool IsRead() const {
return std::tie(offset, index, type, is_bindless) < return is_read;
std::tie(rhs.offset, rhs.index, rhs.type, rhs.is_bindless); }
constexpr bool IsWritten() const {
return is_written;
}
constexpr std::pair<u32, u32> GetBindlessCBuf() const {
return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)};
} }
private: private:
std::size_t offset{}; u64 offset{};
std::size_t index{}; std::size_t index{};
Tegra::Shader::ImageType type{}; Tegra::Shader::ImageType type{};
bool is_bindless{}; bool is_bindless{};
bool is_read{};
bool is_written{};
}; };
struct GlobalMemoryBase { struct GlobalMemoryBase {

@ -95,7 +95,7 @@ public:
return used_samplers; return used_samplers;
} }
const std::set<Image>& GetImages() const { const std::map<u64, Image>& GetImages() const {
return used_images; return used_images;
} }
@ -272,10 +272,10 @@ private:
bool is_shadow); bool is_shadow);
/// Accesses an image. /// Accesses an image.
const Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type); Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type);
/// Access a bindless image sampler. /// Access a bindless image sampler.
const Image& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type); Image& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type);
/// Extracts a sequence of bits from a node /// Extracts a sequence of bits from a node
Node BitfieldExtract(Node value, u32 offset, u32 bits); Node BitfieldExtract(Node value, u32 offset, u32 bits);
@ -356,7 +356,7 @@ private:
std::set<Tegra::Shader::Attribute::Index> used_output_attributes; std::set<Tegra::Shader::Attribute::Index> used_output_attributes;
std::map<u32, ConstBuffer> used_cbufs; std::map<u32, ConstBuffer> used_cbufs;
std::set<Sampler> used_samplers; std::set<Sampler> used_samplers;
std::set<Image> used_images; std::map<u64, Image> used_images;
std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{}; std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances> used_clip_distances{};
std::map<GlobalMemoryBase, GlobalMemoryUsage> used_global_memory; std::map<GlobalMemoryBase, GlobalMemoryUsage> used_global_memory;
bool uses_layer{}; bool uses_layer{};

@ -195,18 +195,18 @@ public:
virtual void DownloadTexture(std::vector<u8>& staging_buffer) = 0; virtual void DownloadTexture(std::vector<u8>& staging_buffer) = 0;
void MarkAsModified(const bool is_modified_, const u64 tick) { void MarkAsModified(bool is_modified_, u64 tick) {
is_modified = is_modified_ || is_target; is_modified = is_modified_ || is_target;
modification_tick = tick; modification_tick = tick;
} }
void MarkAsRenderTarget(const bool is_target, const u32 index) { void MarkAsRenderTarget(bool is_target_, u32 index_) {
this->is_target = is_target; is_target = is_target_;
this->index = index; index = index_;
} }
void MarkAsPicked(const bool is_picked) { void MarkAsPicked(bool is_picked_) {
this->is_picked = is_picked; is_picked = is_picked_;
} }
bool IsModified() const { bool IsModified() const {

@ -24,55 +24,62 @@ using VideoCore::Surface::SurfaceTarget;
using VideoCore::Surface::SurfaceTargetFromTextureType; using VideoCore::Surface::SurfaceTargetFromTextureType;
using VideoCore::Surface::SurfaceType; using VideoCore::Surface::SurfaceType;
SurfaceTarget TextureType2SurfaceTarget(Tegra::Shader::TextureType type, bool is_array) { namespace {
SurfaceTarget TextureTypeToSurfaceTarget(Tegra::Shader::TextureType type, bool is_array) {
switch (type) { switch (type) {
case Tegra::Shader::TextureType::Texture1D: { case Tegra::Shader::TextureType::Texture1D:
if (is_array) return is_array ? SurfaceTarget::Texture1DArray : SurfaceTarget::Texture1D;
return SurfaceTarget::Texture1DArray; case Tegra::Shader::TextureType::Texture2D:
else return is_array ? SurfaceTarget::Texture2DArray : SurfaceTarget::Texture2D;
return SurfaceTarget::Texture1D; case Tegra::Shader::TextureType::Texture3D:
}
case Tegra::Shader::TextureType::Texture2D: {
if (is_array)
return SurfaceTarget::Texture2DArray;
else
return SurfaceTarget::Texture2D;
}
case Tegra::Shader::TextureType::Texture3D: {
ASSERT(!is_array); ASSERT(!is_array);
return SurfaceTarget::Texture3D; return SurfaceTarget::Texture3D;
} case Tegra::Shader::TextureType::TextureCube:
case Tegra::Shader::TextureType::TextureCube: { return is_array ? SurfaceTarget::TextureCubeArray : SurfaceTarget::TextureCubemap;
if (is_array) default:
return SurfaceTarget::TextureCubeArray;
else
return SurfaceTarget::TextureCubemap;
}
default: {
UNREACHABLE(); UNREACHABLE();
return SurfaceTarget::Texture2D; return SurfaceTarget::Texture2D;
} }
}
SurfaceTarget ImageTypeToSurfaceTarget(Tegra::Shader::ImageType type) {
switch (type) {
case Tegra::Shader::ImageType::Texture1D:
return SurfaceTarget::Texture1D;
case Tegra::Shader::ImageType::TextureBuffer:
return SurfaceTarget::TextureBuffer;
case Tegra::Shader::ImageType::Texture1DArray:
return SurfaceTarget::Texture1DArray;
case Tegra::Shader::ImageType::Texture2D:
return SurfaceTarget::Texture2D;
case Tegra::Shader::ImageType::Texture2DArray:
return SurfaceTarget::Texture2DArray;
case Tegra::Shader::ImageType::Texture3D:
return SurfaceTarget::Texture3D;
default:
UNREACHABLE();
return SurfaceTarget::Texture2D;
} }
} }
namespace {
constexpr u32 GetMipmapSize(bool uncompressed, u32 mip_size, u32 tile) { constexpr u32 GetMipmapSize(bool uncompressed, u32 mip_size, u32 tile) {
return uncompressed ? mip_size : std::max(1U, (mip_size + tile - 1) / tile); return uncompressed ? mip_size : std::max(1U, (mip_size + tile - 1) / tile);
} }
} // Anonymous namespace } // Anonymous namespace
SurfaceParams SurfaceParams::CreateForTexture(Core::System& system, SurfaceParams SurfaceParams::CreateForTexture(const Tegra::Texture::TICEntry& tic,
const Tegra::Texture::FullTextureInfo& config,
const VideoCommon::Shader::Sampler& entry) { const VideoCommon::Shader::Sampler& entry) {
SurfaceParams params; SurfaceParams params;
params.is_tiled = config.tic.IsTiled(); params.is_tiled = tic.IsTiled();
params.srgb_conversion = config.tic.IsSrgbConversionEnabled(); params.srgb_conversion = tic.IsSrgbConversionEnabled();
params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0, params.block_width = params.is_tiled ? tic.BlockWidth() : 0,
params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0, params.block_height = params.is_tiled ? tic.BlockHeight() : 0,
params.block_depth = params.is_tiled ? config.tic.BlockDepth() : 0, params.block_depth = params.is_tiled ? tic.BlockDepth() : 0,
params.tile_width_spacing = params.is_tiled ? (1 << config.tic.tile_width_spacing.Value()) : 1; params.tile_width_spacing = params.is_tiled ? (1 << tic.tile_width_spacing.Value()) : 1;
params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(), params.pixel_format =
params.srgb_conversion); PixelFormatFromTextureFormat(tic.format, tic.r_type.Value(), params.srgb_conversion);
params.type = GetFormatType(params.pixel_format); params.type = GetFormatType(params.pixel_format);
if (entry.IsShadow() && params.type == SurfaceType::ColorTexture) { if (entry.IsShadow() && params.type == SurfaceType::ColorTexture) {
switch (params.pixel_format) { switch (params.pixel_format) {
@ -92,31 +99,72 @@ SurfaceParams SurfaceParams::CreateForTexture(Core::System& system,
} }
params.type = GetFormatType(params.pixel_format); params.type = GetFormatType(params.pixel_format);
} }
params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value()); params.component_type = ComponentTypeFromTexture(tic.r_type.Value());
params.type = GetFormatType(params.pixel_format); params.type = GetFormatType(params.pixel_format);
// TODO: on 1DBuffer we should use the tic info. // TODO: on 1DBuffer we should use the tic info.
if (!config.tic.IsBuffer()) { if (tic.IsBuffer()) {
params.target = TextureType2SurfaceTarget(entry.GetType(), entry.IsArray());
params.width = config.tic.Width();
params.height = config.tic.Height();
params.depth = config.tic.Depth();
params.pitch = params.is_tiled ? 0 : config.tic.Pitch();
if (params.target == SurfaceTarget::TextureCubemap ||
params.target == SurfaceTarget::TextureCubeArray) {
params.depth *= 6;
}
params.num_levels = config.tic.max_mip_level + 1;
params.emulated_levels = std::min(params.num_levels, params.MaxPossibleMipmap());
params.is_layered = params.IsLayered();
} else {
params.target = SurfaceTarget::TextureBuffer; params.target = SurfaceTarget::TextureBuffer;
params.width = config.tic.Width(); params.width = tic.Width();
params.pitch = params.width * params.GetBytesPerPixel(); params.pitch = params.width * params.GetBytesPerPixel();
params.height = 1; params.height = 1;
params.depth = 1; params.depth = 1;
params.num_levels = 1; params.num_levels = 1;
params.emulated_levels = 1; params.emulated_levels = 1;
params.is_layered = false; params.is_layered = false;
} else {
params.target = TextureTypeToSurfaceTarget(entry.GetType(), entry.IsArray());
params.width = tic.Width();
params.height = tic.Height();
params.depth = tic.Depth();
params.pitch = params.is_tiled ? 0 : tic.Pitch();
if (params.target == SurfaceTarget::TextureCubemap ||
params.target == SurfaceTarget::TextureCubeArray) {
params.depth *= 6;
}
params.num_levels = tic.max_mip_level + 1;
params.emulated_levels = std::min(params.num_levels, params.MaxPossibleMipmap());
params.is_layered = params.IsLayered();
}
return params;
}
SurfaceParams SurfaceParams::CreateForImage(const Tegra::Texture::TICEntry& tic,
const VideoCommon::Shader::Image& entry) {
SurfaceParams params;
params.is_tiled = tic.IsTiled();
params.srgb_conversion = tic.IsSrgbConversionEnabled();
params.block_width = params.is_tiled ? tic.BlockWidth() : 0,
params.block_height = params.is_tiled ? tic.BlockHeight() : 0,
params.block_depth = params.is_tiled ? tic.BlockDepth() : 0,
params.tile_width_spacing = params.is_tiled ? (1 << tic.tile_width_spacing.Value()) : 1;
params.pixel_format =
PixelFormatFromTextureFormat(tic.format, tic.r_type.Value(), params.srgb_conversion);
params.type = GetFormatType(params.pixel_format);
params.component_type = ComponentTypeFromTexture(tic.r_type.Value());
params.type = GetFormatType(params.pixel_format);
params.target = ImageTypeToSurfaceTarget(entry.GetType());
// TODO: on 1DBuffer we should use the tic info.
if (tic.IsBuffer()) {
params.target = SurfaceTarget::TextureBuffer;
params.width = tic.Width();
params.pitch = params.width * params.GetBytesPerPixel();
params.height = 1;
params.depth = 1;
params.num_levels = 1;
params.emulated_levels = 1;
params.is_layered = false;
} else {
params.width = tic.Width();
params.height = tic.Height();
params.depth = tic.Depth();
params.pitch = params.is_tiled ? 0 : tic.Pitch();
if (params.target == SurfaceTarget::TextureCubemap ||
params.target == SurfaceTarget::TextureCubeArray) {
params.depth *= 6;
}
params.num_levels = tic.max_mip_level + 1;
params.emulated_levels = std::min(params.num_levels, params.MaxPossibleMipmap());
params.is_layered = params.IsLayered();
} }
return params; return params;
} }

@ -4,8 +4,6 @@
#pragma once #pragma once
#include <map>
#include "common/alignment.h" #include "common/alignment.h"
#include "common/bit_util.h" #include "common/bit_util.h"
#include "common/cityhash.h" #include "common/cityhash.h"
@ -23,10 +21,13 @@ using VideoCore::Surface::SurfaceCompression;
class SurfaceParams { class SurfaceParams {
public: public:
/// Creates SurfaceCachedParams from a texture configuration. /// Creates SurfaceCachedParams from a texture configuration.
static SurfaceParams CreateForTexture(Core::System& system, static SurfaceParams CreateForTexture(const Tegra::Texture::TICEntry& tic,
const Tegra::Texture::FullTextureInfo& config,
const VideoCommon::Shader::Sampler& entry); const VideoCommon::Shader::Sampler& entry);
/// Creates SurfaceCachedParams from an image configuration.
static SurfaceParams CreateForImage(const Tegra::Texture::TICEntry& tic,
const VideoCommon::Shader::Image& entry);
/// Creates SurfaceCachedParams for a depth buffer configuration. /// Creates SurfaceCachedParams for a depth buffer configuration.
static SurfaceParams CreateForDepthBuffer( static SurfaceParams CreateForDepthBuffer(
Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format, Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format,

@ -10,7 +10,7 @@
namespace VideoCommon { namespace VideoCommon {
std::size_t ViewParams::Hash() const { std::size_t ViewParams::Hash() const {
return static_cast<std::size_t>(base_layer) ^ static_cast<std::size_t>(num_layers << 16) ^ return static_cast<std::size_t>(base_layer) ^ (static_cast<std::size_t>(num_layers) << 16) ^
(static_cast<std::size_t>(base_level) << 24) ^ (static_cast<std::size_t>(base_level) << 24) ^
(static_cast<std::size_t>(num_levels) << 32) ^ (static_cast<std::size_t>(target) << 36); (static_cast<std::size_t>(num_levels) << 32) ^ (static_cast<std::size_t>(target) << 36);
} }

@ -13,8 +13,8 @@
namespace VideoCommon { namespace VideoCommon {
struct ViewParams { struct ViewParams {
ViewParams(VideoCore::Surface::SurfaceTarget target, u32 base_layer, u32 num_layers, constexpr explicit ViewParams(VideoCore::Surface::SurfaceTarget target, u32 base_layer,
u32 base_level, u32 num_levels) u32 num_layers, u32 base_level, u32 num_levels)
: target{target}, base_layer{base_layer}, num_layers{num_layers}, base_level{base_level}, : target{target}, base_layer{base_layer}, num_layers{num_layers}, base_level{base_level},
num_levels{num_levels} {} num_levels{num_levels} {}
@ -22,12 +22,6 @@ struct ViewParams {
bool operator==(const ViewParams& rhs) const; bool operator==(const ViewParams& rhs) const;
VideoCore::Surface::SurfaceTarget target{};
u32 base_layer{};
u32 num_layers{};
u32 base_level{};
u32 num_levels{};
bool IsLayered() const { bool IsLayered() const {
switch (target) { switch (target) {
case VideoCore::Surface::SurfaceTarget::Texture1DArray: case VideoCore::Surface::SurfaceTarget::Texture1DArray:
@ -39,13 +33,19 @@ struct ViewParams {
return false; return false;
} }
} }
VideoCore::Surface::SurfaceTarget target{};
u32 base_layer{};
u32 num_layers{};
u32 base_level{};
u32 num_levels{};
}; };
class ViewBase { class ViewBase {
public: public:
ViewBase(const ViewParams& params) : params{params} {} constexpr explicit ViewBase(const ViewParams& params) : params{params} {}
const ViewParams& GetViewParams() const { constexpr const ViewParams& GetViewParams() const {
return params; return params;
} }

@ -89,14 +89,29 @@ public:
} }
} }
TView GetTextureSurface(const Tegra::Texture::FullTextureInfo& config, TView GetTextureSurface(const Tegra::Texture::TICEntry& tic,
const VideoCommon::Shader::Sampler& entry) { const VideoCommon::Shader::Sampler& entry) {
std::lock_guard lock{mutex}; std::lock_guard lock{mutex};
const auto gpu_addr{config.tic.Address()}; const auto gpu_addr{tic.Address()};
if (!gpu_addr) { if (!gpu_addr) {
return {}; return {};
} }
const auto params{SurfaceParams::CreateForTexture(system, config, entry)}; const auto params{SurfaceParams::CreateForTexture(tic, entry)};
const auto [surface, view] = GetSurface(gpu_addr, params, true, false);
if (guard_samplers) {
sampled_textures.push_back(surface);
}
return view;
}
TView GetImageSurface(const Tegra::Texture::TICEntry& tic,
const VideoCommon::Shader::Image& entry) {
std::lock_guard lock{mutex};
const auto gpu_addr{tic.Address()};
if (!gpu_addr) {
return {};
}
const auto params{SurfaceParams::CreateForImage(tic, entry)};
const auto [surface, view] = GetSurface(gpu_addr, params, true, false); const auto [surface, view] = GetSurface(gpu_addr, params, true, false);
if (guard_samplers) { if (guard_samplers) {
sampled_textures.push_back(surface); sampled_textures.push_back(surface);