Merge pull request #314 from jroweboy/tegra-progress-3b
GPU: Bind uploaded textures when drawing (Rebased)
This commit is contained in:
commit
227bc78cbe
|
@ -231,6 +231,8 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
|
||||||
|
|
||||||
// TODO(Subv): Different data types for separate components are not supported
|
// TODO(Subv): Different data types for separate components are not supported
|
||||||
ASSERT(r_type == g_type && r_type == b_type && r_type == a_type);
|
ASSERT(r_type == g_type && r_type == b_type && r_type == a_type);
|
||||||
|
// TODO(Subv): Only UNORM formats are supported for now.
|
||||||
|
ASSERT(r_type == Texture::ComponentType::UNORM);
|
||||||
|
|
||||||
return tic_entry;
|
return tic_entry;
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,12 @@ RasterizerOpenGL::RasterizerOpenGL() {
|
||||||
has_ARB_separate_shader_objects = false;
|
has_ARB_separate_shader_objects = false;
|
||||||
has_ARB_vertex_attrib_binding = false;
|
has_ARB_vertex_attrib_binding = false;
|
||||||
|
|
||||||
|
// Create sampler objects
|
||||||
|
for (size_t i = 0; i < texture_samplers.size(); ++i) {
|
||||||
|
texture_samplers[i].Create();
|
||||||
|
state.texture_units[i].sampler = texture_samplers[i].sampler.handle;
|
||||||
|
}
|
||||||
|
|
||||||
GLint ext_num;
|
GLint ext_num;
|
||||||
glGetIntegerv(GL_NUM_EXTENSIONS, &ext_num);
|
glGetIntegerv(GL_NUM_EXTENSIONS, &ext_num);
|
||||||
for (GLint i = 0; i < ext_num; i++) {
|
for (GLint i = 0; i < ext_num; i++) {
|
||||||
|
@ -270,7 +276,9 @@ void RasterizerOpenGL::DrawArrays() {
|
||||||
|
|
||||||
// TODO(bunnei): Sync framebuffer_scale uniform here
|
// TODO(bunnei): Sync framebuffer_scale uniform here
|
||||||
// TODO(bunnei): Sync scissorbox uniform(s) here
|
// TODO(bunnei): Sync scissorbox uniform(s) here
|
||||||
// TODO(bunnei): Sync and bind the texture surfaces
|
|
||||||
|
// Sync and bind the texture surfaces
|
||||||
|
BindTextures();
|
||||||
|
|
||||||
// Sync and bind the shader
|
// Sync and bind the shader
|
||||||
if (shader_dirty) {
|
if (shader_dirty) {
|
||||||
|
@ -374,6 +382,39 @@ void RasterizerOpenGL::DrawArrays() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RasterizerOpenGL::BindTextures() {
|
||||||
|
using Regs = Tegra::Engines::Maxwell3D::Regs;
|
||||||
|
auto maxwell3d = Core::System::GetInstance().GPU().Get3DEngine();
|
||||||
|
|
||||||
|
// Each Maxwell shader stage can have an arbitrary number of textures, but we're limited to a
|
||||||
|
// certain number in OpenGL. We try to only use the minimum amount of host textures by not
|
||||||
|
// keeping a 1:1 relation between guest texture ids and host texture ids, ie, guest texture id 8
|
||||||
|
// can be host texture id 0 if it's the only texture used in the guest shader program.
|
||||||
|
u32 host_texture_index = 0;
|
||||||
|
for (u32 stage = 0; stage < Regs::MaxShaderStage; ++stage) {
|
||||||
|
ASSERT(host_texture_index < texture_samplers.size());
|
||||||
|
const auto textures = maxwell3d.GetStageTextures(static_cast<Regs::ShaderStage>(stage));
|
||||||
|
for (unsigned texture_index = 0; texture_index < textures.size(); ++texture_index) {
|
||||||
|
const auto& texture = textures[texture_index];
|
||||||
|
|
||||||
|
if (texture.enabled) {
|
||||||
|
texture_samplers[host_texture_index].SyncWithConfig(texture.tsc);
|
||||||
|
Surface surface = res_cache.GetTextureSurface(texture);
|
||||||
|
if (surface != nullptr) {
|
||||||
|
state.texture_units[host_texture_index].texture_2d = surface->texture.handle;
|
||||||
|
} else {
|
||||||
|
// Can occur when texture addr is null or its memory is unmapped/invalid
|
||||||
|
state.texture_units[texture_index].texture_2d = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
++host_texture_index;
|
||||||
|
} else {
|
||||||
|
state.texture_units[texture_index].texture_2d = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void RasterizerOpenGL::NotifyMaxwellRegisterChanged(u32 id) {}
|
void RasterizerOpenGL::NotifyMaxwellRegisterChanged(u32 id) {}
|
||||||
|
|
||||||
void RasterizerOpenGL::FlushAll() {
|
void RasterizerOpenGL::FlushAll() {
|
||||||
|
@ -452,6 +493,44 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& framebu
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RasterizerOpenGL::SamplerInfo::Create() {
|
||||||
|
sampler.Create();
|
||||||
|
mag_filter = min_filter = Tegra::Texture::TextureFilter::Linear;
|
||||||
|
wrap_u = wrap_v = Tegra::Texture::WrapMode::Wrap;
|
||||||
|
border_color_r = border_color_g = border_color_b = border_color_a = 0;
|
||||||
|
|
||||||
|
// default is GL_LINEAR_MIPMAP_LINEAR
|
||||||
|
glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
// Other attributes have correct defaults
|
||||||
|
}
|
||||||
|
|
||||||
|
void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) {
|
||||||
|
GLuint s = sampler.handle;
|
||||||
|
|
||||||
|
if (mag_filter != config.mag_filter) {
|
||||||
|
mag_filter = config.mag_filter;
|
||||||
|
glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, MaxwellToGL::TextureFilterMode(mag_filter));
|
||||||
|
}
|
||||||
|
if (min_filter != config.min_filter) {
|
||||||
|
min_filter = config.min_filter;
|
||||||
|
glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, MaxwellToGL::TextureFilterMode(min_filter));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wrap_u != config.wrap_u) {
|
||||||
|
wrap_u = config.wrap_u;
|
||||||
|
glSamplerParameteri(s, GL_TEXTURE_WRAP_S, MaxwellToGL::WrapMode(wrap_u));
|
||||||
|
}
|
||||||
|
if (wrap_v != config.wrap_v) {
|
||||||
|
wrap_v = config.wrap_v;
|
||||||
|
glSamplerParameteri(s, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(wrap_v));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border) {
|
||||||
|
// TODO(Subv): Implement border color
|
||||||
|
ASSERT(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void RasterizerOpenGL::SetShader() {
|
void RasterizerOpenGL::SetShader() {
|
||||||
// TODO(bunnei): The below sets up a static test shader for passing untransformed vertices to
|
// TODO(bunnei): The below sets up a static test shader for passing untransformed vertices to
|
||||||
// OpenGL for rendering. This should be removed/replaced when we start emulating Maxwell
|
// OpenGL for rendering. This should be removed/replaced when we start emulating Maxwell
|
||||||
|
@ -479,10 +558,10 @@ void main() {
|
||||||
in vec2 frag_tex_coord;
|
in vec2 frag_tex_coord;
|
||||||
out vec4 color;
|
out vec4 color;
|
||||||
|
|
||||||
uniform sampler2D color_texture;
|
uniform sampler2D tex[32];
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
color = vec4(1.0, 0.0, 1.0, 0.0);
|
color = texture(tex[0], frag_tex_coord);
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
|
@ -503,6 +582,15 @@ void main() {
|
||||||
state.draw.shader_program = test_shader.shader.handle;
|
state.draw.shader_program = test_shader.shader.handle;
|
||||||
state.Apply();
|
state.Apply();
|
||||||
|
|
||||||
|
for (u32 texture = 0; texture < texture_samplers.size(); ++texture) {
|
||||||
|
// Set the texture samplers to correspond to different texture units
|
||||||
|
std::string uniform_name = "tex[" + std::to_string(texture) + "]";
|
||||||
|
GLint uniform_tex = glGetUniformLocation(test_shader.shader.handle, uniform_name.c_str());
|
||||||
|
if (uniform_tex != -1) {
|
||||||
|
glUniform1i(uniform_tex, TextureUnits::MaxwellTexture(texture).id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (has_ARB_separate_shader_objects) {
|
if (has_ARB_separate_shader_objects) {
|
||||||
state.draw.shader_program = 0;
|
state.draw.shader_program = 0;
|
||||||
state.Apply();
|
state.Apply();
|
||||||
|
|
|
@ -85,12 +85,34 @@ public:
|
||||||
"FSUniformData structure must be less than 16kb as per the OpenGL spec");
|
"FSUniformData structure must be less than 16kb as per the OpenGL spec");
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct SamplerInfo {};
|
class SamplerInfo {
|
||||||
|
public:
|
||||||
|
OGLSampler sampler;
|
||||||
|
|
||||||
|
/// Creates the sampler object, initializing its state so that it's in sync with the
|
||||||
|
/// SamplerInfo struct.
|
||||||
|
void Create();
|
||||||
|
/// Syncs the sampler object with the config, updating any necessary state.
|
||||||
|
void SyncWithConfig(const Tegra::Texture::TSCEntry& config);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Tegra::Texture::TextureFilter mag_filter;
|
||||||
|
Tegra::Texture::TextureFilter min_filter;
|
||||||
|
Tegra::Texture::WrapMode wrap_u;
|
||||||
|
Tegra::Texture::WrapMode wrap_v;
|
||||||
|
u32 border_color_r;
|
||||||
|
u32 border_color_g;
|
||||||
|
u32 border_color_b;
|
||||||
|
u32 border_color_a;
|
||||||
|
};
|
||||||
|
|
||||||
/// Binds the framebuffer color and depth surface
|
/// Binds the framebuffer color and depth surface
|
||||||
void BindFramebufferSurfaces(const Surface& color_surface, const Surface& depth_surface,
|
void BindFramebufferSurfaces(const Surface& color_surface, const Surface& depth_surface,
|
||||||
bool has_stencil);
|
bool has_stencil);
|
||||||
|
|
||||||
|
/// Binds the required textures to OpenGL before drawing a batch.
|
||||||
|
void BindTextures();
|
||||||
|
|
||||||
/// Syncs the viewport to match the guest state
|
/// Syncs the viewport to match the guest state
|
||||||
void SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect, u16 res_scale);
|
void SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect, u16 res_scale);
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include "video_core/engines/maxwell_3d.h"
|
#include "video_core/engines/maxwell_3d.h"
|
||||||
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
|
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
|
||||||
#include "video_core/renderer_opengl/gl_state.h"
|
#include "video_core/renderer_opengl/gl_state.h"
|
||||||
|
#include "video_core/textures/decoders.h"
|
||||||
#include "video_core/utils.h"
|
#include "video_core/utils.h"
|
||||||
#include "video_core/video_core.h"
|
#include "video_core/video_core.h"
|
||||||
|
|
||||||
|
@ -40,36 +41,36 @@ struct FormatTuple {
|
||||||
GLint internal_format;
|
GLint internal_format;
|
||||||
GLenum format;
|
GLenum format;
|
||||||
GLenum type;
|
GLenum type;
|
||||||
|
bool compressed;
|
||||||
|
// How many pixels in the original texture are equivalent to one pixel in the compressed
|
||||||
|
// texture.
|
||||||
|
u32 compression_factor;
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr std::array<FormatTuple, 5> fb_format_tuples = {{
|
static constexpr std::array<FormatTuple, 1> fb_format_tuples = {{
|
||||||
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8}, // RGBA8
|
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, false, 1}, // RGBA8
|
||||||
{GL_RGB8, GL_BGR, GL_UNSIGNED_BYTE}, // RGB8
|
|
||||||
{GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // RGB5A1
|
|
||||||
{GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // RGB565
|
|
||||||
{GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4}, // RGBA4
|
|
||||||
}};
|
}};
|
||||||
|
|
||||||
static constexpr std::array<FormatTuple, 4> depth_format_tuples = {{
|
static constexpr std::array<FormatTuple, 2> tex_format_tuples = {{
|
||||||
{GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16
|
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, false, 1}, // RGBA8
|
||||||
{},
|
{GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, true, 16}, // DXT1
|
||||||
{GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT}, // D24
|
|
||||||
{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // D24S8
|
|
||||||
}};
|
}};
|
||||||
|
|
||||||
static constexpr FormatTuple tex_tuple = {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE};
|
|
||||||
|
|
||||||
static const FormatTuple& GetFormatTuple(PixelFormat pixel_format) {
|
static const FormatTuple& GetFormatTuple(PixelFormat pixel_format) {
|
||||||
const SurfaceType type = SurfaceParams::GetFormatType(pixel_format);
|
const SurfaceType type = SurfaceParams::GetFormatType(pixel_format);
|
||||||
if (type == SurfaceType::Color) {
|
if (type == SurfaceType::Color) {
|
||||||
ASSERT(static_cast<size_t>(pixel_format) < fb_format_tuples.size());
|
ASSERT(static_cast<size_t>(pixel_format) < fb_format_tuples.size());
|
||||||
return fb_format_tuples[static_cast<unsigned int>(pixel_format)];
|
return fb_format_tuples[static_cast<unsigned int>(pixel_format)];
|
||||||
} else if (type == SurfaceType::Depth || type == SurfaceType::DepthStencil) {
|
} else if (type == SurfaceType::Depth || type == SurfaceType::DepthStencil) {
|
||||||
size_t tuple_idx = static_cast<size_t>(pixel_format) - 14;
|
// TODO(Subv): Implement depth formats
|
||||||
ASSERT(tuple_idx < depth_format_tuples.size());
|
ASSERT_MSG(false, "Unimplemented");
|
||||||
return depth_format_tuples[tuple_idx];
|
} else if (type == SurfaceType::Texture) {
|
||||||
|
ASSERT(static_cast<size_t>(pixel_format) < tex_format_tuples.size());
|
||||||
|
return tex_format_tuples[static_cast<unsigned int>(pixel_format)];
|
||||||
}
|
}
|
||||||
return tex_tuple;
|
|
||||||
|
UNREACHABLE();
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Map, typename Interval>
|
template <typename Map, typename Interval>
|
||||||
|
@ -92,26 +93,16 @@ static void MortonCopyTile(u32 stride, u8* tile_buffer, u8* gl_buffer) {
|
||||||
u8* tile_ptr = tile_buffer + VideoCore::MortonInterleave(x, y) * bytes_per_pixel;
|
u8* tile_ptr = tile_buffer + VideoCore::MortonInterleave(x, y) * bytes_per_pixel;
|
||||||
u8* gl_ptr = gl_buffer + ((7 - y) * stride + x) * gl_bytes_per_pixel;
|
u8* gl_ptr = gl_buffer + ((7 - y) * stride + x) * gl_bytes_per_pixel;
|
||||||
if (morton_to_gl) {
|
if (morton_to_gl) {
|
||||||
if (format == PixelFormat::D24S8) {
|
|
||||||
gl_ptr[0] = tile_ptr[3];
|
|
||||||
std::memcpy(gl_ptr + 1, tile_ptr, 3);
|
|
||||||
} else {
|
|
||||||
std::memcpy(gl_ptr, tile_ptr, bytes_per_pixel);
|
std::memcpy(gl_ptr, tile_ptr, bytes_per_pixel);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (format == PixelFormat::D24S8) {
|
|
||||||
std::memcpy(tile_ptr, gl_ptr + 1, 3);
|
|
||||||
tile_ptr[3] = gl_ptr[0];
|
|
||||||
} else {
|
} else {
|
||||||
std::memcpy(tile_ptr, gl_ptr, bytes_per_pixel);
|
std::memcpy(tile_ptr, gl_ptr, bytes_per_pixel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool morton_to_gl, PixelFormat format>
|
template <bool morton_to_gl, PixelFormat format>
|
||||||
static void MortonCopy(u32 stride, u32 height, u8* gl_buffer, VAddr base, VAddr start, VAddr end) {
|
void MortonCopy(u32 stride, u32 height, u8* gl_buffer, VAddr base, VAddr start, VAddr end) {
|
||||||
constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / 8;
|
constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / 8;
|
||||||
constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format);
|
constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format);
|
||||||
|
|
||||||
|
@ -122,46 +113,28 @@ static void MortonCopy(u32 stride, u32 height, u8* gl_buffer, VAddr base, VAddr
|
||||||
Memory::GetPointer(base), gl_buffer, morton_to_gl);
|
Memory::GetPointer(base), gl_buffer, morton_to_gl);
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr std::array<void (*)(u32, u32, u8*, VAddr, VAddr, VAddr), 18> morton_to_gl_fns = {
|
template <>
|
||||||
|
void MortonCopy<true, PixelFormat::DXT1>(u32 stride, u32 height, u8* gl_buffer, VAddr base,
|
||||||
|
VAddr start, VAddr end) {
|
||||||
|
constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(PixelFormat::DXT1) / 8;
|
||||||
|
constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(PixelFormat::DXT1);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!");
|
||||||
|
auto data =
|
||||||
|
Tegra::Texture::UnswizzleTexture(base, Tegra::Texture::TextureFormat::DXT1, stride, height);
|
||||||
|
std::memcpy(gl_buffer, data.data(), data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr std::array<void (*)(u32, u32, u8*, VAddr, VAddr, VAddr), 2> morton_to_gl_fns = {
|
||||||
MortonCopy<true, PixelFormat::RGBA8>,
|
MortonCopy<true, PixelFormat::RGBA8>,
|
||||||
nullptr,
|
MortonCopy<true, PixelFormat::DXT1>,
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr std::array<void (*)(u32, u32, u8*, VAddr, VAddr, VAddr), 18> gl_to_morton_fns = {
|
static constexpr std::array<void (*)(u32, u32, u8*, VAddr, VAddr, VAddr), 2> gl_to_morton_fns = {
|
||||||
MortonCopy<false, PixelFormat::RGBA8>,
|
MortonCopy<false, PixelFormat::RGBA8>,
|
||||||
nullptr,
|
MortonCopy<false, PixelFormat::DXT1>,
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Allocate an uninitialized texture of appropriate size and format for the surface
|
// Allocate an uninitialized texture of appropriate size and format for the surface
|
||||||
|
@ -175,8 +148,11 @@ static void AllocateSurfaceTexture(GLuint texture, const FormatTuple& format_tup
|
||||||
cur_state.Apply();
|
cur_state.Apply();
|
||||||
glActiveTexture(GL_TEXTURE0);
|
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,
|
glTexImage2D(GL_TEXTURE_2D, 0, format_tuple.internal_format, width, height, 0,
|
||||||
format_tuple.format, format_tuple.type, nullptr);
|
format_tuple.format, format_tuple.type, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
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_MIN_FILTER, GL_LINEAR);
|
||||||
|
@ -606,9 +582,18 @@ void CachedSurface::UploadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint
|
||||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(stride));
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(stride));
|
||||||
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
if (tuple.compressed) {
|
||||||
|
glCompressedTexImage2D(GL_TEXTURE_2D, 0, tuple.internal_format,
|
||||||
|
static_cast<GLsizei>(rect.GetWidth()),
|
||||||
|
static_cast<GLsizei>(rect.GetHeight()), 0,
|
||||||
|
rect.GetWidth() * rect.GetHeight() *
|
||||||
|
GetGLBytesPerPixel(pixel_format) / tuple.compression_factor,
|
||||||
|
&gl_buffer[buffer_offset]);
|
||||||
|
} else {
|
||||||
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);
|
||||||
|
|
||||||
|
@ -954,15 +939,6 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, ScaleMatc
|
||||||
if (expandable != nullptr && expandable->res_scale > target_res_scale) {
|
if (expandable != nullptr && expandable->res_scale > target_res_scale) {
|
||||||
target_res_scale = expandable->res_scale;
|
target_res_scale = expandable->res_scale;
|
||||||
}
|
}
|
||||||
// Keep res_scale when reinterpreting d24s8 -> rgba8
|
|
||||||
if (params.pixel_format == PixelFormat::RGBA8) {
|
|
||||||
find_params.pixel_format = PixelFormat::D24S8;
|
|
||||||
expandable = FindMatch<MatchFlags::Expand | MatchFlags::Invalid>(
|
|
||||||
surface_cache, find_params, match_res_scale);
|
|
||||||
if (expandable != nullptr && expandable->res_scale > target_res_scale) {
|
|
||||||
target_res_scale = expandable->res_scale;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SurfaceParams new_params = params;
|
SurfaceParams new_params = params;
|
||||||
new_params.res_scale = target_res_scale;
|
new_params.res_scale = target_res_scale;
|
||||||
|
@ -1056,9 +1032,34 @@ SurfaceRect_Tuple RasterizerCacheOpenGL::GetSurfaceSubRect(const SurfaceParams&
|
||||||
return std::make_tuple(surface, surface->GetScaledSubRect(params));
|
return std::make_tuple(surface, surface->GetScaledSubRect(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
Surface RasterizerCacheOpenGL::GetTextureSurface(const void* config) {
|
Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextureInfo& config) {
|
||||||
UNREACHABLE();
|
auto& gpu = Core::System::GetInstance().GPU();
|
||||||
return {};
|
|
||||||
|
SurfaceParams params;
|
||||||
|
params.addr = gpu.memory_manager->PhysicalToVirtualAddress(config.tic.Address());
|
||||||
|
params.width = config.tic.Width();
|
||||||
|
params.height = config.tic.Height();
|
||||||
|
params.is_tiled = config.tic.IsTiled();
|
||||||
|
params.pixel_format = SurfaceParams::PixelFormatFromTextureFormat(config.tic.format);
|
||||||
|
params.UpdateParams();
|
||||||
|
|
||||||
|
if (config.tic.Width() % 8 != 0 || config.tic.Height() % 8 != 0) {
|
||||||
|
Surface src_surface;
|
||||||
|
MathUtil::Rectangle<u32> rect;
|
||||||
|
std::tie(src_surface, rect) = GetSurfaceSubRect(params, ScaleMatch::Ignore, true);
|
||||||
|
|
||||||
|
params.res_scale = src_surface->res_scale;
|
||||||
|
Surface tmp_surface = CreateSurface(params);
|
||||||
|
BlitTextures(src_surface->texture.handle, rect, tmp_surface->texture.handle,
|
||||||
|
tmp_surface->GetScaledRect(),
|
||||||
|
SurfaceParams::GetFormatType(params.pixel_format), read_framebuffer.handle,
|
||||||
|
draw_framebuffer.handle);
|
||||||
|
|
||||||
|
remove_surfaces.emplace(tmp_surface);
|
||||||
|
return tmp_surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetSurface(params, ScaleMatch::Ignore, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(
|
SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(
|
||||||
|
@ -1240,27 +1241,6 @@ void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, VAddr addr,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// D24S8 to RGBA8
|
|
||||||
if (surface->pixel_format == PixelFormat::RGBA8) {
|
|
||||||
params.pixel_format = PixelFormat::D24S8;
|
|
||||||
Surface reinterpret_surface =
|
|
||||||
FindMatch<MatchFlags::Copy>(surface_cache, params, ScaleMatch::Ignore, interval);
|
|
||||||
if (reinterpret_surface != nullptr) {
|
|
||||||
ASSERT(reinterpret_surface->pixel_format == PixelFormat::D24S8);
|
|
||||||
|
|
||||||
SurfaceInterval convert_interval = params.GetCopyableInterval(reinterpret_surface);
|
|
||||||
SurfaceParams convert_params = surface->FromInterval(convert_interval);
|
|
||||||
auto src_rect = reinterpret_surface->GetScaledSubRect(convert_params);
|
|
||||||
auto dest_rect = surface->GetScaledSubRect(convert_params);
|
|
||||||
|
|
||||||
ConvertD24S8toABGR(reinterpret_surface->texture.handle, src_rect,
|
|
||||||
surface->texture.handle, dest_rect);
|
|
||||||
|
|
||||||
surface->invalid_regions.erase(convert_interval);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load data from Switch memory
|
// Load data from Switch memory
|
||||||
FlushRegion(params.addr, params.size);
|
FlushRegion(params.addr, params.size);
|
||||||
surface->LoadGLBuffer(params.addr, params.end);
|
surface->LoadGLBuffer(params.addr, params.end);
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "common/math_util.h"
|
#include "common/math_util.h"
|
||||||
#include "video_core/gpu.h"
|
#include "video_core/gpu.h"
|
||||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||||
|
#include "video_core/textures/texture.h"
|
||||||
|
|
||||||
struct CachedSurface;
|
struct CachedSurface;
|
||||||
using Surface = std::shared_ptr<CachedSurface>;
|
using Surface = std::shared_ptr<CachedSurface>;
|
||||||
|
@ -51,30 +52,8 @@ enum class ScaleMatch {
|
||||||
|
|
||||||
struct SurfaceParams {
|
struct SurfaceParams {
|
||||||
enum class PixelFormat {
|
enum class PixelFormat {
|
||||||
// First 5 formats are shared between textures and color buffers
|
|
||||||
RGBA8 = 0,
|
RGBA8 = 0,
|
||||||
RGB8 = 1,
|
DXT1 = 1,
|
||||||
RGB5A1 = 2,
|
|
||||||
RGB565 = 3,
|
|
||||||
RGBA4 = 4,
|
|
||||||
|
|
||||||
// Texture-only formats
|
|
||||||
IA8 = 5,
|
|
||||||
RG8 = 6,
|
|
||||||
I8 = 7,
|
|
||||||
A8 = 8,
|
|
||||||
IA4 = 9,
|
|
||||||
I4 = 10,
|
|
||||||
A4 = 11,
|
|
||||||
ETC1 = 12,
|
|
||||||
ETC1A4 = 13,
|
|
||||||
|
|
||||||
// Depth buffer-only formats
|
|
||||||
D16 = 14,
|
|
||||||
// gap
|
|
||||||
D24 = 16,
|
|
||||||
D24S8 = 17,
|
|
||||||
|
|
||||||
Invalid = 255,
|
Invalid = 255,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -88,28 +67,15 @@ struct SurfaceParams {
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr unsigned int GetFormatBpp(PixelFormat format) {
|
static constexpr unsigned int GetFormatBpp(PixelFormat format) {
|
||||||
constexpr std::array<unsigned int, 18> bpp_table = {
|
if (format == PixelFormat::Invalid)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
constexpr std::array<unsigned int, 2> bpp_table = {
|
||||||
32, // RGBA8
|
32, // RGBA8
|
||||||
24, // RGB8
|
64, // DXT1
|
||||||
16, // RGB5A1
|
|
||||||
16, // RGB565
|
|
||||||
16, // RGBA4
|
|
||||||
16, // IA8
|
|
||||||
16, // RG8
|
|
||||||
8, // I8
|
|
||||||
8, // A8
|
|
||||||
8, // IA4
|
|
||||||
4, // I4
|
|
||||||
4, // A4
|
|
||||||
4, // ETC1
|
|
||||||
8, // ETC1A4
|
|
||||||
16, // D16
|
|
||||||
0,
|
|
||||||
24, // D24
|
|
||||||
32, // D24S8
|
|
||||||
};
|
};
|
||||||
|
|
||||||
assert(static_cast<size_t>(format) < bpp_table.size());
|
ASSERT(static_cast<size_t>(format) < bpp_table.size());
|
||||||
return bpp_table[static_cast<size_t>(format)];
|
return bpp_table[static_cast<size_t>(format)];
|
||||||
}
|
}
|
||||||
unsigned int GetFormatBpp() const {
|
unsigned int GetFormatBpp() const {
|
||||||
|
@ -134,6 +100,18 @@ struct SurfaceParams {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format) {
|
||||||
|
// TODO(Subv): Properly implement this
|
||||||
|
switch (format) {
|
||||||
|
case Tegra::Texture::TextureFormat::A8R8G8B8:
|
||||||
|
return PixelFormat::RGBA8;
|
||||||
|
case Tegra::Texture::TextureFormat::DXT1:
|
||||||
|
return PixelFormat::DXT1;
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool CheckFormatsBlittable(PixelFormat pixel_format_a, PixelFormat pixel_format_b) {
|
static bool CheckFormatsBlittable(PixelFormat pixel_format_a, PixelFormat pixel_format_b) {
|
||||||
SurfaceType a_type = GetFormatType(pixel_format_a);
|
SurfaceType a_type = GetFormatType(pixel_format_a);
|
||||||
SurfaceType b_type = GetFormatType(pixel_format_b);
|
SurfaceType b_type = GetFormatType(pixel_format_b);
|
||||||
|
@ -154,22 +132,17 @@ struct SurfaceParams {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr SurfaceType GetFormatType(PixelFormat pixel_format) {
|
static SurfaceType GetFormatType(PixelFormat pixel_format) {
|
||||||
if ((unsigned int)pixel_format < 5) {
|
if ((unsigned int)pixel_format <= static_cast<unsigned int>(PixelFormat::RGBA8)) {
|
||||||
return SurfaceType::Color;
|
return SurfaceType::Color;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((unsigned int)pixel_format < 14) {
|
if ((unsigned int)pixel_format <= static_cast<unsigned int>(PixelFormat::DXT1)) {
|
||||||
return SurfaceType::Texture;
|
return SurfaceType::Texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pixel_format == PixelFormat::D16 || pixel_format == PixelFormat::D24) {
|
// TODO(Subv): Implement the other formats
|
||||||
return SurfaceType::Depth;
|
ASSERT(false);
|
||||||
}
|
|
||||||
|
|
||||||
if (pixel_format == PixelFormat::D24S8) {
|
|
||||||
return SurfaceType::DepthStencil;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SurfaceType::Invalid;
|
return SurfaceType::Invalid;
|
||||||
}
|
}
|
||||||
|
@ -265,12 +238,10 @@ struct CachedSurface : SurfaceParams {
|
||||||
OGLTexture texture;
|
OGLTexture texture;
|
||||||
|
|
||||||
static constexpr unsigned int GetGLBytesPerPixel(PixelFormat format) {
|
static constexpr unsigned int GetGLBytesPerPixel(PixelFormat format) {
|
||||||
// OpenGL needs 4 bpp alignment for D24 since using GL_UNSIGNED_INT as type
|
if (format == PixelFormat::Invalid)
|
||||||
return format == PixelFormat::Invalid
|
return 0;
|
||||||
? 0
|
|
||||||
: (format == PixelFormat::D24 || GetFormatType(format) == SurfaceType::Texture)
|
return SurfaceParams::GetFormatBpp(format) / 8;
|
||||||
? 4
|
|
||||||
: SurfaceParams::GetFormatBpp(format) / 8;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<u8[]> gl_buffer;
|
std::unique_ptr<u8[]> gl_buffer;
|
||||||
|
@ -313,7 +284,7 @@ public:
|
||||||
bool load_if_create);
|
bool load_if_create);
|
||||||
|
|
||||||
/// Get a surface based on the texture configuration
|
/// Get a surface based on the texture configuration
|
||||||
Surface GetTextureSurface(const void* config);
|
Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config);
|
||||||
|
|
||||||
/// Get the color and depth surfaces based on the framebuffer configuration
|
/// Get the color and depth surfaces based on the framebuffer configuration
|
||||||
SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb,
|
SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb,
|
||||||
|
|
|
@ -194,7 +194,7 @@ void OpenGLState::Apply() const {
|
||||||
// Textures
|
// Textures
|
||||||
for (unsigned i = 0; i < ARRAY_SIZE(texture_units); ++i) {
|
for (unsigned i = 0; i < ARRAY_SIZE(texture_units); ++i) {
|
||||||
if (texture_units[i].texture_2d != cur_state.texture_units[i].texture_2d) {
|
if (texture_units[i].texture_2d != cur_state.texture_units[i].texture_2d) {
|
||||||
glActiveTexture(TextureUnits::PicaTexture(i).Enum());
|
glActiveTexture(TextureUnits::MaxwellTexture(i).Enum());
|
||||||
glBindTexture(GL_TEXTURE_2D, texture_units[i].texture_2d);
|
glBindTexture(GL_TEXTURE_2D, texture_units[i].texture_2d);
|
||||||
}
|
}
|
||||||
if (texture_units[i].sampler != cur_state.texture_units[i].sampler) {
|
if (texture_units[i].sampler != cur_state.texture_units[i].sampler) {
|
||||||
|
|
|
@ -16,7 +16,7 @@ struct TextureUnit {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr TextureUnit PicaTexture(int unit) {
|
constexpr TextureUnit MaxwellTexture(int unit) {
|
||||||
return TextureUnit{unit};
|
return TextureUnit{unit};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,4 +47,27 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode) {
|
||||||
|
switch (filter_mode) {
|
||||||
|
case Tegra::Texture::TextureFilter::Linear:
|
||||||
|
return GL_LINEAR;
|
||||||
|
case Tegra::Texture::TextureFilter::Nearest:
|
||||||
|
return GL_NEAREST;
|
||||||
|
}
|
||||||
|
LOG_CRITICAL(Render_OpenGL, "Unimplemented texture filter mode=%u",
|
||||||
|
static_cast<u32>(filter_mode));
|
||||||
|
UNREACHABLE();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
|
||||||
|
switch (wrap_mode) {
|
||||||
|
case Tegra::Texture::WrapMode::ClampToEdge:
|
||||||
|
return GL_CLAMP_TO_EDGE;
|
||||||
|
}
|
||||||
|
LOG_CRITICAL(Render_OpenGL, "Unimplemented texture wrap mode=%u", static_cast<u32>(wrap_mode));
|
||||||
|
UNREACHABLE();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace MaxwellToGL
|
} // namespace MaxwellToGL
|
||||||
|
|
|
@ -37,6 +37,16 @@ enum class TICHeaderVersion : u32 {
|
||||||
BlockLinearColorKey = 4,
|
BlockLinearColorKey = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class ComponentType : u32 {
|
||||||
|
SNORM = 1,
|
||||||
|
UNORM = 2,
|
||||||
|
SINT = 3,
|
||||||
|
UINT = 4,
|
||||||
|
SNORM_FORCE_FP16 = 5,
|
||||||
|
UNORM_FORCE_FP16 = 6,
|
||||||
|
FLOAT = 7
|
||||||
|
};
|
||||||
|
|
||||||
union TextureHandle {
|
union TextureHandle {
|
||||||
u32 raw;
|
u32 raw;
|
||||||
BitField<0, 20, u32> tic_id;
|
BitField<0, 20, u32> tic_id;
|
||||||
|
@ -48,10 +58,10 @@ struct TICEntry {
|
||||||
union {
|
union {
|
||||||
u32 raw;
|
u32 raw;
|
||||||
BitField<0, 7, TextureFormat> format;
|
BitField<0, 7, TextureFormat> format;
|
||||||
BitField<7, 3, u32> r_type;
|
BitField<7, 3, ComponentType> r_type;
|
||||||
BitField<10, 3, u32> g_type;
|
BitField<10, 3, ComponentType> g_type;
|
||||||
BitField<13, 3, u32> b_type;
|
BitField<13, 3, ComponentType> b_type;
|
||||||
BitField<16, 3, u32> a_type;
|
BitField<16, 3, ComponentType> a_type;
|
||||||
};
|
};
|
||||||
u32 address_low;
|
u32 address_low;
|
||||||
union {
|
union {
|
||||||
|
@ -77,6 +87,11 @@ struct TICEntry {
|
||||||
u32 Height() const {
|
u32 Height() const {
|
||||||
return height_minus_1 + 1;
|
return height_minus_1 + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsTiled() const {
|
||||||
|
return header_version == TICHeaderVersion::BlockLinear ||
|
||||||
|
header_version == TICHeaderVersion::BlockLinearColorKey;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
static_assert(sizeof(TICEntry) == 0x20, "TICEntry has wrong size");
|
static_assert(sizeof(TICEntry) == 0x20, "TICEntry has wrong size");
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue