citra-emu
/
citra-canary
Archived
1
0
Fork 0

Merge pull request #1104 from yuriks/opengl-samplers

OpenGL: Use Sampler Objects to decouple sampler config from textures
This commit is contained in:
bunnei 2015-09-05 16:19:52 -04:00
commit 51e91dfd86
11 changed files with 133 additions and 36 deletions

View File

@ -72,6 +72,9 @@ void EmuThread::run() {
} }
} }
// Shutdown the core emulation
System::Shutdown();
MicroProfileOnThreadExit(); MicroProfileOnThreadExit();
render_window->moveContext(); render_window->moveContext();

View File

@ -283,9 +283,6 @@ void GMainWindow::ShutdownGame() {
emu_thread->wait(); emu_thread->wait();
emu_thread = nullptr; emu_thread = nullptr;
// Shutdown the core emulation
System::Shutdown();
// Update the GUI // Update the GUI
ui.action_Start->setEnabled(false); ui.action_Start->setEnabled(false);
ui.action_Start->setText(tr("Start")); ui.action_Start->setText(tr("Start"));

View File

@ -135,6 +135,7 @@ struct Regs {
}; };
union { union {
u32 raw;
BitField< 0, 8, u32> r; BitField< 0, 8, u32> r;
BitField< 8, 8, u32> g; BitField< 8, 8, u32> g;
BitField<16, 8, u32> b; BitField<16, 8, u32> b;
@ -339,6 +340,7 @@ struct Regs {
}; };
union { union {
u32 const_color;
BitField< 0, 8, u32> const_r; BitField< 0, 8, u32> const_r;
BitField< 8, 8, u32> const_g; BitField< 8, 8, u32> const_g;
BitField<16, 8, u32> const_b; BitField<16, 8, u32> const_b;
@ -389,6 +391,7 @@ struct Regs {
TevStageConfig tev_stage5; TevStageConfig tev_stage5;
union { union {
u32 raw;
BitField< 0, 8, u32> r; BitField< 0, 8, u32> r;
BitField< 8, 8, u32> g; BitField< 8, 8, u32> g;
BitField<16, 8, u32> b; BitField<16, 8, u32> b;
@ -473,6 +476,7 @@ struct Regs {
}; };
union { union {
u32 raw;
BitField< 0, 8, u32> r; BitField< 0, 8, u32> r;
BitField< 8, 8, u32> g; BitField< 8, 8, u32> g;
BitField<16, 8, u32> b; BitField<16, 8, u32> b;

View File

@ -68,6 +68,12 @@ void RasterizerOpenGL::InitObjects() {
uniform_tev_cfg.updates_combiner_buffer_color_alpha = glGetUniformLocation(shader.handle, (tev_ref_str + ".updates_combiner_buffer_color_alpha").c_str()); uniform_tev_cfg.updates_combiner_buffer_color_alpha = glGetUniformLocation(shader.handle, (tev_ref_str + ".updates_combiner_buffer_color_alpha").c_str());
} }
// Create sampler objects
for (int i = 0; i < texture_samplers.size(); ++i) {
texture_samplers[i].Create();
state.texture_units[i].sampler = texture_samplers[i].sampler.handle;
}
// Generate VBO and VAO // Generate VBO and VAO
vertex_buffer.Create(); vertex_buffer.Create();
vertex_array.Create(); vertex_array.Create();
@ -445,6 +451,45 @@ void RasterizerOpenGL::NotifyFlush(PAddr addr, u32 size) {
res_cache.NotifyFlush(addr, size); res_cache.NotifyFlush(addr, size);
} }
void RasterizerOpenGL::SamplerInfo::Create() {
sampler.Create();
mag_filter = min_filter = TextureConfig::Linear;
wrap_s = wrap_t = TextureConfig::Repeat;
border_color = 0;
glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // default is GL_LINEAR_MIPMAP_LINEAR
// Other attributes have correct defaults
}
void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Pica::Regs::TextureConfig& config) {
GLuint s = sampler.handle;
if (mag_filter != config.mag_filter) {
mag_filter = config.mag_filter;
glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, PicaToGL::TextureFilterMode(mag_filter));
}
if (min_filter != config.min_filter) {
min_filter = config.min_filter;
glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, PicaToGL::TextureFilterMode(min_filter));
}
if (wrap_s != config.wrap_s) {
wrap_s = config.wrap_s;
glSamplerParameteri(s, GL_TEXTURE_WRAP_S, PicaToGL::WrapMode(wrap_s));
}
if (wrap_t != config.wrap_t) {
wrap_t = config.wrap_t;
glSamplerParameteri(s, GL_TEXTURE_WRAP_T, PicaToGL::WrapMode(wrap_t));
}
if (wrap_s == TextureConfig::ClampToBorder || wrap_t == TextureConfig::ClampToBorder) {
if (border_color != config.border_color.raw) {
auto gl_color = PicaToGL::ColorRGBA8(border_color);
glSamplerParameterfv(s, GL_TEXTURE_BORDER_COLOR, gl_color.data());
}
}
}
void RasterizerOpenGL::ReconfigureColorTexture(TextureInfo& texture, Pica::Regs::ColorFormat format, u32 width, u32 height) { void RasterizerOpenGL::ReconfigureColorTexture(TextureInfo& texture, Pica::Regs::ColorFormat format, u32 width, u32 height) {
GLint internal_format; GLint internal_format;
@ -658,7 +703,7 @@ void RasterizerOpenGL::SyncBlendFuncs() {
} }
void RasterizerOpenGL::SyncBlendColor() { void RasterizerOpenGL::SyncBlendColor() {
auto blend_color = PicaToGL::ColorRGBA8((u8*)&Pica::g_state.regs.output_merger.blend_const.r); auto blend_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.output_merger.blend_const.raw);
state.blend.color.red = blend_color[0]; state.blend.color.red = blend_color[0];
state.blend.color.green = blend_color[1]; state.blend.color.green = blend_color[1];
state.blend.color.blue = blend_color[2]; state.blend.color.blue = blend_color[2];
@ -728,7 +773,7 @@ void RasterizerOpenGL::SyncTevOps(unsigned stage_index, const Pica::Regs::TevSta
} }
void RasterizerOpenGL::SyncTevColor(unsigned stage_index, const Pica::Regs::TevStageConfig& config) { void RasterizerOpenGL::SyncTevColor(unsigned stage_index, const Pica::Regs::TevStageConfig& config) {
auto const_color = PicaToGL::ColorRGBA8((u8*)&config.const_r); auto const_color = PicaToGL::ColorRGBA8(config.const_color);
glUniform4fv(uniform_tev_cfgs[stage_index].const_color, 1, const_color.data()); glUniform4fv(uniform_tev_cfgs[stage_index].const_color, 1, const_color.data());
} }
@ -737,7 +782,7 @@ void RasterizerOpenGL::SyncTevMultipliers(unsigned stage_index, const Pica::Regs
} }
void RasterizerOpenGL::SyncCombinerColor() { void RasterizerOpenGL::SyncCombinerColor() {
auto combiner_color = PicaToGL::ColorRGBA8((u8*)&Pica::g_state.regs.tev_combiner_buffer_color.r); auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw);
glUniform4fv(uniform_tev_combiner_buffer_color, 1, combiner_color.data()); glUniform4fv(uniform_tev_combiner_buffer_color, 1, combiner_color.data());
} }
@ -772,6 +817,7 @@ void RasterizerOpenGL::SyncDrawState() {
const auto& texture = pica_textures[texture_index]; const auto& texture = pica_textures[texture_index];
if (texture.enabled) { if (texture.enabled) {
texture_samplers[texture_index].SyncWithConfig(texture.config);
res_cache.LoadAndBindTexture(state, texture_index, texture); res_cache.LoadAndBindTexture(state, texture_index, texture);
} else { } else {
state.texture_units[texture_index].texture_2d = 0; state.texture_units[texture_index].texture_2d = 0;

View File

@ -80,6 +80,24 @@ private:
GLenum gl_type; GLenum gl_type;
}; };
struct SamplerInfo {
using TextureConfig = Pica::Regs::TextureConfig;
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 TextureConfig& config);
private:
TextureConfig::TextureFilter mag_filter;
TextureConfig::TextureFilter min_filter;
TextureConfig::WrapMode wrap_s;
TextureConfig::WrapMode wrap_t;
u32 border_color;
};
/// Structure that the hardware rendered vertices are composed of /// Structure that the hardware rendered vertices are composed of
struct HardwareVertex { struct HardwareVertex {
HardwareVertex(const Pica::Shader::OutputVertex& v) { HardwareVertex(const Pica::Shader::OutputVertex& v) {
@ -193,6 +211,7 @@ private:
PAddr last_fb_depth_addr; PAddr last_fb_depth_addr;
// Hardware rasterizer // Hardware rasterizer
std::array<SamplerInfo, 3> texture_samplers;
TextureInfo fb_color_texture; TextureInfo fb_color_texture;
DepthTextureInfo fb_depth_texture; DepthTextureInfo fb_depth_texture;
OGLShader shader; OGLShader shader;

View File

@ -20,9 +20,8 @@ RasterizerCacheOpenGL::~RasterizerCacheOpenGL() {
MICROPROFILE_DEFINE(OpenGL_TextureUpload, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192)); MICROPROFILE_DEFINE(OpenGL_TextureUpload, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192));
void RasterizerCacheOpenGL::LoadAndBindTexture(OpenGLState &state, unsigned texture_unit, const Pica::Regs::FullTextureConfig& config) { void RasterizerCacheOpenGL::LoadAndBindTexture(OpenGLState &state, unsigned texture_unit, const Pica::DebugUtils::TextureInfo& info) {
PAddr texture_addr = config.config.GetPhysicalAddress(); const auto cached_texture = texture_cache.find(info.physical_address);
const auto cached_texture = texture_cache.find(texture_addr);
if (cached_texture != texture_cache.end()) { if (cached_texture != texture_cache.end()) {
state.texture_units[texture_unit].texture_2d = cached_texture->second->texture.handle; state.texture_units[texture_unit].texture_2d = cached_texture->second->texture.handle;
@ -37,26 +36,12 @@ void RasterizerCacheOpenGL::LoadAndBindTexture(OpenGLState &state, unsigned text
state.Apply(); state.Apply();
glActiveTexture(GL_TEXTURE0 + texture_unit); glActiveTexture(GL_TEXTURE0 + texture_unit);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, PicaToGL::TextureFilterMode(config.config.mag_filter)); u8* texture_src_data = Memory::GetPhysicalPointer(info.physical_address);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, PicaToGL::TextureFilterMode(config.config.min_filter));
GLenum wrap_s = PicaToGL::WrapMode(config.config.wrap_s);
GLenum wrap_t = PicaToGL::WrapMode(config.config.wrap_t);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t);
if (wrap_s == GL_CLAMP_TO_BORDER || wrap_t == GL_CLAMP_TO_BORDER) {
auto border_color = PicaToGL::ColorRGBA8((u8*)&config.config.border_color.r);
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color.data());
}
const auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config.config, config.format);
u8* texture_src_data = Memory::GetPhysicalPointer(texture_addr);
new_texture->width = info.width; new_texture->width = info.width;
new_texture->height = info.height; new_texture->height = info.height;
new_texture->size = info.stride * info.height; new_texture->size = info.stride * info.height;
new_texture->addr = texture_addr; new_texture->addr = info.physical_address;
new_texture->hash = Common::ComputeHash64(texture_src_data, new_texture->size); new_texture->hash = Common::ComputeHash64(texture_src_data, new_texture->size);
std::unique_ptr<Math::Vec4<u8>[]> temp_texture_buffer_rgba(new Math::Vec4<u8>[info.width * info.height]); std::unique_ptr<Math::Vec4<u8>[]> temp_texture_buffer_rgba(new Math::Vec4<u8>[info.width * info.height]);
@ -69,7 +54,7 @@ void RasterizerCacheOpenGL::LoadAndBindTexture(OpenGLState &state, unsigned text
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, info.width, info.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, temp_texture_buffer_rgba.get()); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, info.width, info.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, temp_texture_buffer_rgba.get());
texture_cache.emplace(texture_addr, std::move(new_texture)); texture_cache.emplace(info.physical_address, std::move(new_texture));
} }
} }

View File

@ -6,6 +6,7 @@
#include "gl_state.h" #include "gl_state.h"
#include "gl_resource_manager.h" #include "gl_resource_manager.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/pica.h" #include "video_core/pica.h"
#include <memory> #include <memory>
@ -16,7 +17,11 @@ public:
~RasterizerCacheOpenGL(); ~RasterizerCacheOpenGL();
/// Loads a texture from 3DS memory to OpenGL and caches it (if not already cached) /// Loads a texture from 3DS memory to OpenGL and caches it (if not already cached)
void LoadAndBindTexture(OpenGLState &state, unsigned texture_unit, const Pica::Regs::FullTextureConfig& config); void LoadAndBindTexture(OpenGLState &state, unsigned texture_unit, const Pica::DebugUtils::TextureInfo& info);
void LoadAndBindTexture(OpenGLState &state, unsigned texture_unit, const Pica::Regs::FullTextureConfig& config) {
LoadAndBindTexture(state, texture_unit, Pica::DebugUtils::TextureInfo::FromPicaRegister(config.config, config.format));
}
/// Flush any cached resource that touches the flushed region /// Flush any cached resource that touches the flushed region
void NotifyFlush(PAddr addr, u32 size, bool ignore_hash = false); void NotifyFlush(PAddr addr, u32 size, bool ignore_hash = false);

View File

@ -37,6 +37,30 @@ public:
GLuint handle = 0; GLuint handle = 0;
}; };
class OGLSampler : private NonCopyable {
public:
OGLSampler() = default;
OGLSampler(OGLSampler&& o) { std::swap(handle, o.handle); }
~OGLSampler() { Release(); }
OGLSampler& operator=(OGLSampler&& o) { std::swap(handle, o.handle); return *this; }
/// Creates a new internal OpenGL resource and stores the handle
void Create() {
if (handle != 0) return;
glGenSamplers(1, &handle);
}
/// Deletes the internal OpenGL resource
void Release() {
if (handle == 0) return;
glDeleteSamplers(1, &handle);
OpenGLState::ResetSampler(handle);
handle = 0;
}
GLuint handle = 0;
};
class OGLShader : private NonCopyable { class OGLShader : private NonCopyable {
public: public:
OGLShader() = default; OGLShader() = default;

View File

@ -44,6 +44,7 @@ OpenGLState::OpenGLState() {
for (auto& texture_unit : texture_units) { for (auto& texture_unit : texture_units) {
texture_unit.texture_2d = 0; texture_unit.texture_2d = 0;
texture_unit.sampler = 0;
} }
draw.framebuffer = 0; draw.framebuffer = 0;
@ -154,10 +155,13 @@ void OpenGLState::Apply() {
} }
// Textures // Textures
for (unsigned texture_index = 0; texture_index < ARRAY_SIZE(texture_units); ++texture_index) { for (unsigned i = 0; i < ARRAY_SIZE(texture_units); ++i) {
if (texture_units[texture_index].texture_2d != cur_state.texture_units[texture_index].texture_2d) { if (texture_units[i].texture_2d != cur_state.texture_units[i].texture_2d) {
glActiveTexture(GL_TEXTURE0 + texture_index); glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, texture_units[texture_index].texture_2d); glBindTexture(GL_TEXTURE_2D, texture_units[i].texture_2d);
}
if (texture_units[i].sampler != cur_state.texture_units[i].sampler) {
glBindSampler(i, texture_units[i].sampler);
} }
} }
@ -192,6 +196,14 @@ void OpenGLState::ResetTexture(GLuint id) {
} }
} }
void OpenGLState::ResetSampler(GLuint id) {
for (auto& unit : cur_state.texture_units) {
if (unit.sampler == id) {
unit.sampler = 0;
}
}
}
void OpenGLState::ResetProgram(GLuint id) { void OpenGLState::ResetProgram(GLuint id) {
if (cur_state.draw.shader_program == id) { if (cur_state.draw.shader_program == id) {
cur_state.draw.shader_program = 0; cur_state.draw.shader_program = 0;

View File

@ -57,6 +57,7 @@ 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 { struct {
GLuint texture_2d; // GL_TEXTURE_BINDING_2D GLuint texture_2d; // GL_TEXTURE_BINDING_2D
GLuint sampler; // GL_SAMPLER_BINDING
} texture_units[3]; } texture_units[3];
struct { struct {
@ -77,6 +78,7 @@ public:
void Apply(); void Apply();
static void ResetTexture(GLuint id); static void ResetTexture(GLuint id);
static void ResetSampler(GLuint id);
static void ResetProgram(GLuint id); static void ResetProgram(GLuint id);
static void ResetBuffer(GLuint id); static void ResetBuffer(GLuint id);
static void ResetVertexArray(GLuint id); static void ResetVertexArray(GLuint id);

View File

@ -175,11 +175,11 @@ inline GLenum StencilOp(Pica::Regs::StencilAction action) {
return stencil_op_table[(unsigned)action]; return stencil_op_table[(unsigned)action];
} }
inline std::array<GLfloat, 4> ColorRGBA8(const u8* bytes) { inline std::array<GLfloat, 4> ColorRGBA8(const u32 color) {
return { { bytes[0] / 255.0f, return { { (color >> 0 & 0xFF) / 255.0f,
bytes[1] / 255.0f, (color >> 8 & 0xFF) / 255.0f,
bytes[2] / 255.0f, (color >> 16 & 0xFF) / 255.0f,
bytes[3] / 255.0f (color >> 24 & 0xFF) / 255.0f
} }; } };
} }