mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-20 11:36:13 +00:00
rsx texture cache reimplementation (wip)
This commit is contained in:
parent
a748e7738a
commit
8e158143db
10 changed files with 727 additions and 1209 deletions
|
@ -108,7 +108,7 @@ struct fnv_1a_hasher
|
|||
}
|
||||
};
|
||||
|
||||
struct binary_equals
|
||||
struct bitwise_equals
|
||||
{
|
||||
template<typename TypeA, typename TypeB>
|
||||
bool operator()(const TypeA& a, const TypeB& b) const
|
||||
|
|
|
@ -389,21 +389,7 @@ void GLGSRender::end()
|
|||
int location;
|
||||
if (m_program->uniforms.has_location("tex" + std::to_string(i), &location))
|
||||
{
|
||||
u32 target = GL_TEXTURE_2D;
|
||||
if (textures[i].format() & CELL_GCM_TEXTURE_UN)
|
||||
target = GL_TEXTURE_RECTANGLE;
|
||||
|
||||
if (!textures[i].enabled())
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + i);
|
||||
glBindTexture(target, NULL);
|
||||
glProgramUniform1i(m_program->id(), location, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
m_gl_textures[i].set_target(target);
|
||||
|
||||
__glcheck m_gl_texture_cache.upload_texture(i, textures[i], m_gl_textures[i]);
|
||||
rsx::gl_texture::bind(m_gl_texture_cache, i, textures[i]);
|
||||
glProgramUniform1i(m_program->id(), location, i);
|
||||
}
|
||||
}
|
||||
|
@ -743,16 +729,14 @@ void GLGSRender::on_init_thread()
|
|||
for (texture_buffer_pair &attrib_buffer : m_gl_attrib_buffers)
|
||||
{
|
||||
gl::texture *&tex = attrib_buffer.texture;
|
||||
tex = new gl::texture(gl::texture::target::textureBuffer);
|
||||
tex = new gl::texture(gl::texture::target::texture_buffer);
|
||||
tex->create();
|
||||
tex->set_target(gl::texture::target::textureBuffer);
|
||||
tex->set_target(gl::texture::target::texture_buffer);
|
||||
|
||||
gl::buffer *&buf = attrib_buffer.buffer;
|
||||
buf = new gl::buffer();
|
||||
buf->create();
|
||||
}
|
||||
|
||||
m_gl_texture_cache.initialize_rtt_cache();
|
||||
}
|
||||
|
||||
void GLGSRender::on_exit()
|
||||
|
@ -1181,6 +1165,8 @@ void GLGSRender::read_buffers()
|
|||
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
|
||||
//TODO
|
||||
#if 0
|
||||
if (rpcs3::state.config.rsx.opengl.read_color_buffers)
|
||||
{
|
||||
auto color_format = surface_color_format_to_gl(m_surface.color_format);
|
||||
|
@ -1304,6 +1290,7 @@ void GLGSRender::read_buffers()
|
|||
|
||||
__glcheck m_draw_tex_depth_stencil.copy_from(pbo_depth, depth_format.second, depth_format.first);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void GLGSRender::write_buffers()
|
||||
|
@ -1311,6 +1298,7 @@ void GLGSRender::write_buffers()
|
|||
if (!draw_fbo)
|
||||
return;
|
||||
|
||||
#if 0
|
||||
if (rpcs3::state.config.rsx.opengl.write_color_buffers)
|
||||
{
|
||||
auto color_format = surface_color_format_to_gl(m_surface.color_format);
|
||||
|
@ -1384,6 +1372,8 @@ void GLGSRender::write_buffers()
|
|||
|
||||
m_gl_texture_cache.save_render_target(depth_address, range, m_draw_tex_depth_stencil);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void GLGSRender::flip(int buffer)
|
||||
|
@ -1418,6 +1408,8 @@ void GLGSRender::flip(int buffer)
|
|||
*/
|
||||
}
|
||||
|
||||
//TODO
|
||||
|
||||
if (!skip_read)
|
||||
{
|
||||
if (!m_flip_tex_color || m_flip_tex_color.size() != sizei{ (int)buffer_width, (int)buffer_height })
|
||||
|
@ -1512,5 +1504,48 @@ u64 GLGSRender::timestamp() const
|
|||
|
||||
bool GLGSRender::on_access_violation(u32 address, bool is_writing)
|
||||
{
|
||||
return m_gl_texture_cache.sync_at((is_writing ? gl::cache_buffers::host : gl::cache_buffers::local), address);
|
||||
if (auto region = m_gl_texture_cache.find_region(address))
|
||||
{
|
||||
if (is_writing)
|
||||
{
|
||||
const bool accurate_cache = false;
|
||||
|
||||
if (accurate_cache)
|
||||
{
|
||||
region->for_each([this](gl::cached_texture& texture)
|
||||
{
|
||||
invoke([&]()
|
||||
{
|
||||
texture.sync(gl::cache_buffers::host);
|
||||
texture.invalidate(gl::cache_buffers::local);
|
||||
});
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
region->for_each([](gl::cached_texture& texture)
|
||||
{
|
||||
texture.invalidate(gl::cache_buffers::local);
|
||||
});
|
||||
}
|
||||
|
||||
region->unprotect();
|
||||
}
|
||||
else
|
||||
{
|
||||
region->for_each([this](gl::cached_texture& texture)
|
||||
{
|
||||
invoke([&]()
|
||||
{
|
||||
texture.sync(gl::cache_buffers::host);
|
||||
});
|
||||
});
|
||||
|
||||
region->unprotect(gl::cache_access::read);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -16,9 +16,6 @@ private:
|
|||
GLFragmentProgram m_fragment_prog;
|
||||
GLVertexProgram m_vertex_prog;
|
||||
|
||||
rsx::gl::texture m_gl_textures[rsx::limits::textures_count];
|
||||
rsx::gl::texture m_gl_vertex_textures[rsx::limits::vertex_textures_count];
|
||||
|
||||
gl::glsl::program *m_program;
|
||||
|
||||
rsx::surface_info m_surface;
|
||||
|
|
|
@ -760,6 +760,10 @@ namespace gl
|
|||
enum class format
|
||||
{
|
||||
red = GL_RED,
|
||||
blue = GL_BLUE,
|
||||
green = GL_GREEN,
|
||||
alpha = GL_ALPHA,
|
||||
|
||||
r = GL_R,
|
||||
rg = GL_RG,
|
||||
rgb = GL_RGB,
|
||||
|
@ -788,6 +792,7 @@ namespace gl
|
|||
stencil8 = GL_STENCIL_INDEX8,
|
||||
depth = GL_DEPTH_COMPONENT,
|
||||
depth16 = GL_DEPTH_COMPONENT16,
|
||||
depth24 = GL_DEPTH_COMPONENT24,
|
||||
depth_stencil = GL_DEPTH_STENCIL,
|
||||
depth24_stencil8 = GL_DEPTH24_STENCIL8,
|
||||
|
||||
|
@ -831,7 +836,8 @@ namespace gl
|
|||
texture1D = GL_TEXTURE_1D,
|
||||
texture2D = GL_TEXTURE_2D,
|
||||
texture3D = GL_TEXTURE_3D,
|
||||
textureBuffer = GL_TEXTURE_BUFFER
|
||||
texture_buffer = GL_TEXTURE_BUFFER,
|
||||
texture_rectangle = GL_TEXTURE_RECTANGLE,
|
||||
};
|
||||
|
||||
enum class channel_type
|
||||
|
@ -867,7 +873,8 @@ namespace gl
|
|||
case target::texture1D: pname = GL_TEXTURE_BINDING_1D; break;
|
||||
case target::texture2D: pname = GL_TEXTURE_BINDING_2D; break;
|
||||
case target::texture3D: pname = GL_TEXTURE_BINDING_3D; break;
|
||||
case target::textureBuffer: pname = GL_TEXTURE_BINDING_BUFFER; break;
|
||||
case target::texture_buffer: pname = GL_TEXTURE_BINDING_BUFFER; break;
|
||||
case target::texture_rectangle: pname = GL_TEXTURE_BINDING_RECTANGLE; break;
|
||||
}
|
||||
|
||||
glGetIntegerv(pname, &m_last_binding);
|
||||
|
@ -1135,7 +1142,7 @@ namespace gl
|
|||
|
||||
void copy_from(buffer &buf, u32 gl_format_type, u32 offset, u32 length)
|
||||
{
|
||||
if (get_target() != target::textureBuffer)
|
||||
if (get_target() != target::texture_buffer)
|
||||
throw EXCEPTION("OpenGL error: texture cannot copy from buffer");
|
||||
|
||||
if (!offset)
|
||||
|
@ -1147,13 +1154,13 @@ namespace gl
|
|||
if (glTextureBufferRangeEXT == nullptr)
|
||||
throw EXCEPTION("OpenGL error: partial buffer access for textures is unsupported on your system");
|
||||
|
||||
__glcheck glTextureBufferRangeEXT(id(), (GLenum)target::textureBuffer, gl_format_type, buf.id(), offset, length);
|
||||
__glcheck glTextureBufferRangeEXT(id(), (GLenum)target::texture_buffer, gl_format_type, buf.id(), offset, length);
|
||||
}
|
||||
|
||||
void copy_from(buffer &buf, u32 gl_format_type)
|
||||
{
|
||||
save_binding_state save(*this);
|
||||
__glcheck glTexBuffer((GLenum)target::textureBuffer, gl_format_type, buf.id());
|
||||
__glcheck glTexBuffer((GLenum)target::texture_buffer, gl_format_type, buf.id());
|
||||
}
|
||||
|
||||
void copy_from(const buffer& buf, texture::format format, texture::type type, class pixel_unpack_settings pixel_settings)
|
||||
|
|
|
@ -14,616 +14,346 @@
|
|||
|
||||
namespace gl
|
||||
{
|
||||
texture_cache::~texture_cache()
|
||||
void cached_texture::read()
|
||||
{
|
||||
clear_obj_cache();
|
||||
}
|
||||
LOG_WARNING(RSX, "cached_texture at 0x%x reading from host buffer", info.start_address);
|
||||
|
||||
bool texture_cache::lock_memory_region(u32 start, u32 size)
|
||||
{
|
||||
start = start & ~(vm::page_size - 1);
|
||||
size = (u32)align(size, vm::page_size);
|
||||
cached_texture* found_texture = nullptr;
|
||||
u32 texture_size = info.size();
|
||||
|
||||
return vm::page_protect(start, size, 0, 0, vm::page_writable);
|
||||
}
|
||||
m_parent_region->for_each(info.start_address, texture_size, [&](cached_texture& texture)
|
||||
{
|
||||
if ((texture.m_state & cache_entry_state::local_synchronized) == cache_entry_state::invalid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool texture_cache::unlock_memory_region(u32 start, u32 size)
|
||||
{
|
||||
start = start & ~(vm::page_size - 1);
|
||||
size = (u32)align(size, vm::page_size);
|
||||
if (texture.info.start_address > info.start_address || texture.info.pitch != info.pitch || texture.info.height < info.height)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
return vm::page_protect(start, size, 0, vm::page_writable, 0);
|
||||
}
|
||||
found_texture = &texture;
|
||||
});
|
||||
|
||||
void texture_cache::lock_gl_object(cached_texture &obj)
|
||||
{
|
||||
obj.protected_block_start = obj.data_addr & ~(vm::page_size - 1);
|
||||
obj.protected_block_sz = (u32)align(obj.block_sz, vm::page_size);
|
||||
if (found_texture)
|
||||
{
|
||||
//read from local
|
||||
|
||||
if (!lock_memory_region(obj.protected_block_start, obj.protected_block_sz))
|
||||
LOG_ERROR(RSX, "lock_gl_object failed!");
|
||||
//TODO
|
||||
}
|
||||
else
|
||||
obj.locked = true;
|
||||
}
|
||||
|
||||
void texture_cache::unlock_gl_object(cached_texture &obj)
|
||||
{
|
||||
if (!unlock_memory_region(obj.protected_block_start, obj.protected_block_sz))
|
||||
LOG_ERROR(RSX, "unlock_gl_object failed! Will probably crash soon...");
|
||||
else
|
||||
obj.locked = false;
|
||||
}
|
||||
|
||||
cached_texture *texture_cache::find_obj_for_params(u64 texaddr, u32 w, u32 h, u16 mipmap)
|
||||
{
|
||||
for (cached_texture &tex : m_texture_cache)
|
||||
{
|
||||
if (tex.gl_id && tex.data_addr == texaddr)
|
||||
//read from host
|
||||
|
||||
//flush all local textures at region
|
||||
m_parent_region->for_each(info.start_address, texture_size, [](cached_texture& texture)
|
||||
{
|
||||
if (w && h && mipmap && (tex.h != h || tex.w != w || tex.mipmap != mipmap))
|
||||
{
|
||||
LOG_ERROR(RSX, "Texture params are invalid for block starting 0x%X!", tex.data_addr);
|
||||
LOG_ERROR(RSX, "Params passed w=%d, h=%d, mip=%d, found w=%d, h=%d, mip=%d", w, h, mipmap, tex.w, tex.h, tex.mipmap);
|
||||
texture.sync(gl::cache_buffers::host);
|
||||
});
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
tex.frame_ctr = m_frame_ctr;
|
||||
return &tex;
|
||||
}
|
||||
//TODO
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
ignore(gl::cache_buffers::all);
|
||||
}
|
||||
|
||||
cached_texture& texture_cache::create_obj_for_params(u32 gl_id, u64 texaddr, u32 w, u32 h, u16 mipmap)
|
||||
void cached_texture::write()
|
||||
{
|
||||
cached_texture obj = { 0 };
|
||||
LOG_WARNING(RSX, "cached_texture at 0x%x writing to host buffer", info.start_address);
|
||||
|
||||
obj.gl_id = gl_id;
|
||||
obj.data_addr = texaddr;
|
||||
obj.w = w;
|
||||
obj.h = h;
|
||||
obj.mipmap = mipmap;
|
||||
obj.deleted = false;
|
||||
obj.locked = false;
|
||||
//TODO
|
||||
|
||||
for (cached_texture &tex : m_texture_cache)
|
||||
ignore(gl::cache_buffers::all);
|
||||
}
|
||||
|
||||
bool cached_texture::sync(cache_buffers buffers)
|
||||
{
|
||||
if (!created())
|
||||
{
|
||||
if (tex.gl_id == 0 || (tex.deleted && (m_frame_ctr - tex.frame_ctr) > 32768))
|
||||
create();
|
||||
}
|
||||
|
||||
switch (m_state)
|
||||
{
|
||||
case cache_entry_state::invalid:
|
||||
case cache_entry_state::host_synchronized:
|
||||
if ((buffers & cache_buffers::local) != cache_buffers::none)
|
||||
{
|
||||
if (tex.gl_id)
|
||||
{
|
||||
LOG_NOTICE(RSX, "Reclaiming GL texture %d, cache_size=%d, master_ctr=%d, ctr=%d", tex.gl_id, m_texture_cache.size(), m_frame_ctr, tex.frame_ctr);
|
||||
__glcheck glDeleteTextures(1, &tex.gl_id);
|
||||
unlock_gl_object(tex);
|
||||
tex.gl_id = 0;
|
||||
}
|
||||
|
||||
tex = obj;
|
||||
return tex;
|
||||
read();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
m_texture_cache.push_back(obj);
|
||||
return m_texture_cache.back();
|
||||
}
|
||||
|
||||
void texture_cache::remove_obj(cached_texture &tex)
|
||||
{
|
||||
if (tex.locked)
|
||||
unlock_gl_object(tex);
|
||||
|
||||
tex.deleted = true;
|
||||
}
|
||||
|
||||
void texture_cache::remove_obj_for_glid(u32 gl_id)
|
||||
{
|
||||
for (cached_texture &tex : m_texture_cache)
|
||||
{
|
||||
if (tex.gl_id == gl_id)
|
||||
remove_obj(tex);
|
||||
}
|
||||
}
|
||||
|
||||
void texture_cache::clear_obj_cache()
|
||||
{
|
||||
for (cached_texture &tex : m_texture_cache)
|
||||
{
|
||||
if (tex.locked)
|
||||
unlock_gl_object(tex);
|
||||
|
||||
if (tex.gl_id)
|
||||
case cache_entry_state::local_synchronized:
|
||||
if ((buffers & cache_buffers::host) != cache_buffers::none)
|
||||
{
|
||||
LOG_NOTICE(RSX, "Deleting texture %d", tex.gl_id);
|
||||
glDeleteTextures(1, &tex.gl_id);
|
||||
write();
|
||||
return true;
|
||||
}
|
||||
|
||||
tex.deleted = true;
|
||||
tex.gl_id = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
m_texture_cache.clear();
|
||||
destroy_rtt_cache();
|
||||
}
|
||||
|
||||
bool texture_cache::region_overlaps(u32 base1, u32 limit1, u32 base2, u32 limit2)
|
||||
{
|
||||
//Check for memory area overlap. unlock page(s) if needed and add this index to array.
|
||||
//Axis separation test
|
||||
const u32 &block_start = base1;
|
||||
const u32 block_end = limit1;
|
||||
|
||||
if (limit2 < block_start) return false;
|
||||
if (base2 > block_end) return false;
|
||||
|
||||
u32 min_separation = (limit2 - base2) + (limit1 - base1);
|
||||
u32 range_limit = (block_end > limit2) ? block_end : limit2;
|
||||
u32 range_base = (block_start < base2) ? block_start : base2;
|
||||
|
||||
u32 actual_separation = (range_limit - range_base);
|
||||
|
||||
if (actual_separation < min_separation)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
cached_rtt* texture_cache::find_cached_rtt(u32 base, u32 size)
|
||||
void cached_texture::invalidate(cache_buffers buffers)
|
||||
{
|
||||
for (cached_rtt &rtt : m_rtt_cache)
|
||||
{
|
||||
if (region_overlaps(base, base + size, rtt.data_addr, rtt.data_addr + rtt.block_sz))
|
||||
{
|
||||
return &rtt;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void texture_cache::invalidate_rtts_in_range(u32 base, u32 size)
|
||||
{
|
||||
for (cached_rtt &rtt : m_rtt_cache)
|
||||
{
|
||||
if (!rtt.data_addr || rtt.is_dirty) continue;
|
||||
|
||||
u32 rtt_aligned_base = u32(rtt.data_addr) & ~(vm::page_size - 1);
|
||||
u32 rtt_block_sz = align(rtt.block_sz, vm::page_size);
|
||||
|
||||
if (region_overlaps(rtt_aligned_base, (rtt_aligned_base + rtt_block_sz), base, base + size))
|
||||
{
|
||||
LOG_NOTICE(RSX, "Dirty RTT FOUND addr=0x%X", base);
|
||||
rtt.is_dirty = true;
|
||||
if (rtt.locked)
|
||||
{
|
||||
rtt.locked = false;
|
||||
unlock_memory_region((u32)rtt.data_addr, rtt.block_sz);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void texture_cache::prep_rtt(cached_rtt &rtt, u32 width, u32 height, u32 gl_pixel_format_internal)
|
||||
{
|
||||
int binding = 0;
|
||||
bool is_depth = false;
|
||||
|
||||
if (gl_pixel_format_internal == GL_DEPTH24_STENCIL8 ||
|
||||
gl_pixel_format_internal == GL_DEPTH_COMPONENT24 ||
|
||||
gl_pixel_format_internal == GL_DEPTH_COMPONENT16 ||
|
||||
gl_pixel_format_internal == GL_DEPTH_COMPONENT32)
|
||||
{
|
||||
is_depth = true;
|
||||
}
|
||||
|
||||
glGetIntegerv(GL_TEXTURE_2D_BINDING_EXT, &binding);
|
||||
glBindTexture(GL_TEXTURE_2D, rtt.copy_glid);
|
||||
|
||||
rtt.current_width = width;
|
||||
rtt.current_height = height;
|
||||
|
||||
if (!is_depth)
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
__glcheck glTexImage2D(GL_TEXTURE_2D, 0, gl_pixel_format_internal, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
|
||||
u32 ex_format = GL_UNSIGNED_SHORT;
|
||||
u32 in_format = GL_DEPTH_COMPONENT16;
|
||||
|
||||
switch (gl_pixel_format_internal)
|
||||
{
|
||||
case GL_DEPTH24_STENCIL8:
|
||||
{
|
||||
ex_format = GL_UNSIGNED_INT_24_8;
|
||||
in_format = GL_DEPTH_STENCIL;
|
||||
break;
|
||||
}
|
||||
case GL_DEPTH_COMPONENT16:
|
||||
break;
|
||||
default:
|
||||
throw EXCEPTION("Unsupported depth format!");
|
||||
}
|
||||
|
||||
__glcheck glTexImage2D(GL_TEXTURE_2D, 0, gl_pixel_format_internal, width, height, 0, in_format, ex_format, nullptr);
|
||||
}
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, binding);
|
||||
rtt.is_depth = is_depth;
|
||||
}
|
||||
|
||||
void texture_cache::save_rtt(u32 base, u32 size, u32 width, u32 height, u32 gl_pixel_format_internal, gl::texture &source)
|
||||
{
|
||||
cached_rtt *region = find_cached_rtt(base, size);
|
||||
|
||||
if (!region)
|
||||
{
|
||||
for (cached_rtt &rtt : m_rtt_cache)
|
||||
{
|
||||
if (rtt.valid && rtt.data_addr == 0)
|
||||
{
|
||||
prep_rtt(rtt, width, height, gl_pixel_format_internal);
|
||||
|
||||
rtt.block_sz = size;
|
||||
rtt.data_addr = base;
|
||||
rtt.is_dirty = true;
|
||||
|
||||
LOG_NOTICE(RSX, "New RTT created for block 0x%X + 0x%X", (u32)rtt.data_addr, rtt.block_sz);
|
||||
|
||||
lock_memory_region((u32)rtt.data_addr, rtt.block_sz);
|
||||
rtt.locked = true;
|
||||
|
||||
region = &rtt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!region) throw EXCEPTION("No region created!!");
|
||||
}
|
||||
|
||||
if (width != region->current_width ||
|
||||
height != region->current_height)
|
||||
{
|
||||
prep_rtt(*region, width, height, gl_pixel_format_internal);
|
||||
|
||||
if (region->locked && region->block_sz != size)
|
||||
{
|
||||
LOG_NOTICE(RSX, "Unlocking RTT since size has changed!");
|
||||
unlock_memory_region((u32)region->data_addr, region->block_sz);
|
||||
|
||||
LOG_NOTICE(RSX, "Locking down RTT after size change!");
|
||||
region->block_sz = size;
|
||||
lock_memory_region((u32)region->data_addr, region->block_sz);
|
||||
region->locked = true;
|
||||
}
|
||||
}
|
||||
|
||||
__glcheck glCopyImageSubData(source.id(), GL_TEXTURE_2D, 0, 0, 0, 0,
|
||||
region->copy_glid, GL_TEXTURE_2D, 0, 0, 0, 0,
|
||||
width, height, 1);
|
||||
|
||||
region->is_dirty = false;
|
||||
|
||||
if (!region->locked)
|
||||
{
|
||||
LOG_WARNING(RSX, "Locking down RTT, was unlocked!");
|
||||
lock_memory_region((u32)region->data_addr, region->block_sz);
|
||||
region->locked = true;
|
||||
}
|
||||
}
|
||||
|
||||
void texture_cache::write_rtt(u32 base, u32 size, u32 texaddr)
|
||||
{
|
||||
//Actually download the data, since it seems that cell is writing to it manually
|
||||
throw;
|
||||
}
|
||||
|
||||
void texture_cache::destroy_rtt_cache()
|
||||
{
|
||||
for (cached_rtt &rtt : m_rtt_cache)
|
||||
{
|
||||
rtt.valid = false;
|
||||
rtt.is_dirty = false;
|
||||
rtt.block_sz = 0;
|
||||
rtt.data_addr = 0;
|
||||
|
||||
glDeleteTextures(1, &rtt.copy_glid);
|
||||
rtt.copy_glid = 0;
|
||||
}
|
||||
|
||||
m_rtt_cache.clear();
|
||||
}
|
||||
|
||||
void texture_cache::update_frame_ctr()
|
||||
{
|
||||
m_frame_ctr++;
|
||||
}
|
||||
|
||||
void texture_cache::initialize_rtt_cache()
|
||||
{
|
||||
if (!m_rtt_cache.empty())
|
||||
{
|
||||
throw EXCEPTION("Initialize RTT cache while cache already exists! Leaking objects??");
|
||||
}
|
||||
|
||||
for (int i = 0; i < 64; ++i)
|
||||
{
|
||||
cached_rtt rtt;
|
||||
|
||||
glGenTextures(1, &rtt.copy_glid);
|
||||
rtt.is_dirty = true;
|
||||
rtt.valid = true;
|
||||
rtt.block_sz = 0;
|
||||
rtt.data_addr = 0;
|
||||
rtt.locked = false;
|
||||
|
||||
m_rtt_cache.push_back(rtt);
|
||||
}
|
||||
}
|
||||
|
||||
void texture_cache::upload_texture(int index, rsx::texture &tex, rsx::gl::texture &gl_texture)
|
||||
{
|
||||
const u32 texaddr = rsx::get_address(tex.offset(), tex.location());
|
||||
const u32 range = (u32)get_texture_size(tex);
|
||||
|
||||
cached_rtt *rtt = find_cached_rtt(texaddr, range);
|
||||
|
||||
if (rtt && !rtt->is_dirty)
|
||||
{
|
||||
if (!rtt->is_depth)
|
||||
{
|
||||
u32 real_id = gl_texture.id();
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + index);
|
||||
gl_texture.set_id(rtt->copy_glid);
|
||||
gl_texture.bind();
|
||||
|
||||
gl_texture.set_id(real_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_NOTICE(RSX, "Depth RTT found from 0x%X, Trying to upload width dims: %d x %d, Saved as %d x %d", rtt->data_addr, tex.width(), tex.height(), rtt->current_width, rtt->current_height);
|
||||
//The texture should have already been loaded through the writeback interface call
|
||||
//Bind it directly
|
||||
u32 real_id = gl_texture.id();
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + index);
|
||||
gl_texture.set_id(rtt->copy_glid);
|
||||
gl_texture.bind();
|
||||
|
||||
gl_texture.set_id(real_id);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if (rtt)
|
||||
{
|
||||
LOG_NOTICE(RSX, "RTT texture for address 0x%X is dirty!", texaddr);
|
||||
}
|
||||
|
||||
cached_texture *obj = nullptr;
|
||||
|
||||
if (!rtt)
|
||||
{
|
||||
obj = find_obj_for_params(texaddr, tex.width(), tex.height(), tex.mipmap());
|
||||
}
|
||||
|
||||
if (obj && !obj->deleted)
|
||||
{
|
||||
u32 real_id = gl_texture.id();
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + index);
|
||||
gl_texture.set_id(obj->gl_id);
|
||||
gl_texture.bind();
|
||||
|
||||
gl_texture.set_id(real_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!obj) gl_texture.set_id(0);
|
||||
else
|
||||
{
|
||||
//Reuse this GLid
|
||||
gl_texture.set_id(obj->gl_id);
|
||||
|
||||
//Empty this slot for another one. A new holder will be created below anyway...
|
||||
if (obj->locked) unlock_gl_object(*obj);
|
||||
obj->gl_id = 0;
|
||||
}
|
||||
|
||||
__glcheck gl_texture.init(index, tex);
|
||||
cached_texture &_obj = create_obj_for_params(gl_texture.id(), texaddr, tex.width(), tex.height(), tex.mipmap());
|
||||
|
||||
_obj.block_sz = (u32)get_texture_size(tex);
|
||||
lock_gl_object(_obj);
|
||||
}
|
||||
}
|
||||
|
||||
bool texture_cache::mark_local_as_dirty_at(u32 address)
|
||||
{
|
||||
bool response = false;
|
||||
|
||||
for (cached_texture &tex : m_texture_cache)
|
||||
{
|
||||
if (!tex.locked)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tex.protected_block_start <= address &&
|
||||
tex.protected_block_sz > (address - tex.protected_block_start))
|
||||
{
|
||||
LOG_NOTICE(RSX, "Texture object is dirty! %d", tex.gl_id);
|
||||
unlock_gl_object(tex);
|
||||
|
||||
invalidate_rtts_in_range((u32)tex.data_addr, tex.block_sz);
|
||||
|
||||
tex.deleted = true;
|
||||
response = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!response)
|
||||
{
|
||||
for (cached_rtt &rtt : m_rtt_cache)
|
||||
{
|
||||
if (!rtt.data_addr || rtt.is_dirty) continue;
|
||||
|
||||
u32 rtt_aligned_base = u32(rtt.data_addr) & ~(vm::page_size - 1);
|
||||
u32 rtt_block_sz = align(rtt.block_sz, vm::page_size);
|
||||
|
||||
if (rtt.locked && (u64)address >= rtt_aligned_base)
|
||||
{
|
||||
u32 offset = address - rtt_aligned_base;
|
||||
if (offset >= rtt_block_sz) continue;
|
||||
|
||||
LOG_NOTICE(RSX, "Dirty non-texture RTT FOUND! addr=0x%X", rtt.data_addr);
|
||||
rtt.is_dirty = true;
|
||||
|
||||
unlock_memory_region(rtt_aligned_base, rtt_block_sz);
|
||||
rtt.locked = false;
|
||||
|
||||
response = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
void texture_cache::save_render_target(u32 texaddr, u32 range, gl::texture &gl_texture)
|
||||
{
|
||||
save_rtt(texaddr, range, gl_texture.width(), gl_texture.height(), (GLenum)gl_texture.get_internal_format(), gl_texture);
|
||||
}
|
||||
|
||||
std::vector<invalid_cache_area> texture_cache::find_and_invalidate_in_range(u32 base, u32 limit)
|
||||
{
|
||||
/**
|
||||
* Sometimes buffers can share physical pages.
|
||||
* Return objects if we really encroach on texture
|
||||
*/
|
||||
|
||||
std::vector<invalid_cache_area> result;
|
||||
|
||||
for (cached_texture &obj : m_texture_cache)
|
||||
{
|
||||
//Check for memory area overlap. unlock page(s) if needed and add this index to array.
|
||||
//Axis separation test
|
||||
const u32 &block_start = obj.protected_block_start;
|
||||
const u32 block_end = block_start + obj.protected_block_sz;
|
||||
|
||||
if (limit < block_start) continue;
|
||||
if (base > block_end) continue;
|
||||
|
||||
u32 min_separation = (limit - base) + obj.protected_block_sz;
|
||||
u32 range_limit = (block_end > limit) ? block_end : limit;
|
||||
u32 range_base = (block_start < base) ? block_start : base;
|
||||
|
||||
u32 actual_separation = (range_limit - range_base);
|
||||
|
||||
if (actual_separation < min_separation)
|
||||
{
|
||||
const u32 texture_start = (u32)obj.data_addr;
|
||||
const u32 texture_end = texture_start + obj.block_sz;
|
||||
|
||||
min_separation = (limit - base) + obj.block_sz;
|
||||
range_limit = (texture_end > limit) ? texture_end : limit;
|
||||
range_base = (texture_start < base) ? texture_start : base;
|
||||
|
||||
actual_separation = (range_limit - range_base);
|
||||
|
||||
if (actual_separation < min_separation)
|
||||
{
|
||||
//Texture area is invalidated!
|
||||
unlock_gl_object(obj);
|
||||
obj.deleted = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
//Overlap in this case will be at most 1 page...
|
||||
invalid_cache_area invalid = { 0 };
|
||||
if (base < obj.data_addr)
|
||||
invalid.block_base = obj.protected_block_start;
|
||||
else
|
||||
invalid.block_base = obj.protected_block_start + obj.protected_block_sz - vm::page_size;
|
||||
|
||||
invalid.block_sz = vm::page_size;
|
||||
unlock_memory_region(invalid.block_base, invalid.block_sz);
|
||||
result.push_back(invalid);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void texture_cache::lock_invalidated_ranges(const std::vector<invalid_cache_area> &invalid)
|
||||
{
|
||||
for (const invalid_cache_area &area : invalid)
|
||||
{
|
||||
lock_memory_region(area.block_base, area.block_sz);
|
||||
}
|
||||
}
|
||||
|
||||
void texture_cache::remove_in_range(u32 texaddr, u32 range)
|
||||
{
|
||||
//Seems that the rsx only 'reads' full texture objects..
|
||||
//This simplifies this function to simply check for matches
|
||||
for (cached_texture &cached : m_texture_cache)
|
||||
{
|
||||
if (cached.data_addr == texaddr &&
|
||||
cached.block_sz == range)
|
||||
remove_obj(cached);
|
||||
}
|
||||
}
|
||||
|
||||
bool texture_cache::explicit_writeback(gl::texture &tex, const u32 address, const u32 pitch)
|
||||
{
|
||||
const u32 range = tex.height() * pitch;
|
||||
cached_rtt *rtt = find_cached_rtt(address, range);
|
||||
|
||||
if (rtt && !rtt->is_dirty)
|
||||
{
|
||||
u32 min_w = rtt->current_width;
|
||||
u32 min_h = rtt->current_height;
|
||||
|
||||
if ((u32)tex.width() < min_w) min_w = (u32)tex.width();
|
||||
if ((u32)tex.height() < min_h) min_h = (u32)tex.height();
|
||||
|
||||
//TODO: Image reinterpretation e.g read back rgba data as depth texture and vice-versa
|
||||
|
||||
__glcheck glCopyImageSubData(rtt->copy_glid, GL_TEXTURE_2D, 0, 0, 0, 0,
|
||||
tex.id(), GL_TEXTURE_2D, 0, 0, 0, 0,
|
||||
min_w, min_h, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//No valid object found in cache
|
||||
return false;
|
||||
}
|
||||
|
||||
bool texture_cache::sync_at(cache_buffers buffers, u32 address)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
if ((buffers & cache_buffers::host) != cache_buffers::none)
|
||||
{
|
||||
result = mark_local_as_dirty_at(address);
|
||||
if ((m_state & cache_entry_state::host_synchronized) != cache_entry_state::invalid)
|
||||
{
|
||||
m_state &= ~cache_entry_state::host_synchronized;
|
||||
|
||||
m_parent_region->m_write_protected++;
|
||||
}
|
||||
|
||||
m_parent_region->for_each(info.start_address, info.size(), [this](cached_texture& texture)
|
||||
{
|
||||
if (std::addressof(texture) != this)
|
||||
{
|
||||
texture.invalidate(cache_buffers::local);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if ((buffers & cache_buffers::local) != cache_buffers::none)
|
||||
{
|
||||
if ((m_state & cache_entry_state::local_synchronized) != cache_entry_state::invalid)
|
||||
{
|
||||
m_state &= ~cache_entry_state::local_synchronized;
|
||||
|
||||
m_parent_region->m_read_protected++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cached_texture::ignore(cache_buffers buffers)
|
||||
{
|
||||
if ((buffers & cache_buffers::host) != cache_buffers::none)
|
||||
{
|
||||
if ((m_state & cache_entry_state::host_synchronized) == cache_entry_state::invalid)
|
||||
{
|
||||
m_state |= cache_entry_state::host_synchronized;
|
||||
|
||||
m_parent_region->m_write_protected--;
|
||||
}
|
||||
}
|
||||
|
||||
if ((buffers & cache_buffers::local) != cache_buffers::none)
|
||||
{
|
||||
if ((m_state & cache_entry_state::local_synchronized) == cache_entry_state::invalid)
|
||||
{
|
||||
m_state |= cache_entry_state::local_synchronized;
|
||||
|
||||
m_parent_region->m_read_protected--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cached_texture::parent(protected_region *region)
|
||||
{
|
||||
m_parent_region = region;
|
||||
}
|
||||
|
||||
bool cached_texture::is_synchronized(cache_buffers buffers) const
|
||||
{
|
||||
if ((buffers & cache_buffers::host) != cache_buffers::none)
|
||||
{
|
||||
if ((m_state & cache_entry_state::host_synchronized) == cache_entry_state::invalid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((buffers & cache_buffers::local) != cache_buffers::none)
|
||||
{
|
||||
if ((m_state & cache_entry_state::local_synchronized) == cache_entry_state::invalid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cached_texture::bind(uint index) const
|
||||
{
|
||||
if (index != ~0u)
|
||||
{
|
||||
glActiveTexture(GL_TEXTURE0 + index);
|
||||
}
|
||||
|
||||
glBindTexture((GLenum)info.target, gl_name);
|
||||
}
|
||||
|
||||
void cached_texture::create()
|
||||
{
|
||||
assert(!created());
|
||||
|
||||
glGenTextures(1, &gl_name);
|
||||
}
|
||||
|
||||
void cached_texture::remove()
|
||||
{
|
||||
if (created())
|
||||
{
|
||||
glDeleteTextures(1, &gl_name);
|
||||
gl_name = 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool cached_texture::created() const
|
||||
{
|
||||
return gl_name != 0;
|
||||
}
|
||||
|
||||
void protected_region::for_each(std::function<void(cached_texture& texture)> callback)
|
||||
{
|
||||
for (auto &entry : m_textures)
|
||||
{
|
||||
callback(entry.second);
|
||||
}
|
||||
}
|
||||
|
||||
void protected_region::for_each(u32 start_address, u32 size, std::function<void(cached_texture& texture)> callback)
|
||||
{
|
||||
for (auto &entry : m_textures)
|
||||
{
|
||||
u32 entry_address = entry.first;
|
||||
cached_texture &entry_texture = entry.second;
|
||||
|
||||
if (entry_address > start_address)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
u32 entry_size = entry_texture.info.size();
|
||||
//TODO
|
||||
|
||||
callback(entry_texture);
|
||||
}
|
||||
}
|
||||
|
||||
void protected_region::protect()
|
||||
{
|
||||
u32 flags = 0;
|
||||
|
||||
if (m_read_protected)
|
||||
{
|
||||
flags |= vm::page_readable;
|
||||
}
|
||||
|
||||
if (m_write_protected)
|
||||
{
|
||||
flags |= vm::page_writable;
|
||||
}
|
||||
|
||||
if (m_current_protection != flags)
|
||||
{
|
||||
vm::page_protect(start_address, size(), 0, flags, m_current_protection & ~flags);
|
||||
m_current_protection = flags;
|
||||
}
|
||||
}
|
||||
|
||||
void protected_region::unprotect(cache_access access)
|
||||
{
|
||||
u32 flags = 0;
|
||||
|
||||
if ((access & cache_access::read) != cache_access::none)
|
||||
{
|
||||
if (m_read_protected)
|
||||
{
|
||||
flags |= vm::page_readable;
|
||||
}
|
||||
}
|
||||
|
||||
if ((access & cache_access::write) != cache_access::none)
|
||||
{
|
||||
if (m_write_protected)
|
||||
{
|
||||
flags |= vm::page_writable;
|
||||
}
|
||||
}
|
||||
|
||||
vm::page_protect(start_address, size(), 0, 0, flags);
|
||||
m_current_protection &= ~flags;
|
||||
}
|
||||
|
||||
inline bool protected_region::empty() const
|
||||
{
|
||||
return m_textures.empty();
|
||||
}
|
||||
|
||||
void protected_region::separate(protected_region& dst)
|
||||
{
|
||||
//TODO
|
||||
}
|
||||
|
||||
void protected_region::combine(const protected_region& region)
|
||||
{
|
||||
//TODO
|
||||
}
|
||||
|
||||
void protected_region::clear()
|
||||
{
|
||||
unprotect();
|
||||
|
||||
for (auto &entry : m_textures)
|
||||
{
|
||||
entry.second.remove();
|
||||
}
|
||||
|
||||
m_textures.clear();
|
||||
|
||||
m_read_protected = 0;
|
||||
m_write_protected = 0;
|
||||
}
|
||||
|
||||
cached_texture &texture_cache::entry(texture_info &info, cache_buffers sync_buffers)
|
||||
{
|
||||
auto region = find_region(info.start_address, info.size());
|
||||
|
||||
if (!region)
|
||||
{
|
||||
region = &m_protected_regions[info.start_address & ~(vm::page_size - 1)];
|
||||
region->pages_count = (info.size() + vm::page_size - 1) / vm::page_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO
|
||||
}
|
||||
|
||||
return result;
|
||||
cached_texture *result = nullptr;
|
||||
|
||||
//TODO
|
||||
|
||||
result->sync(sync_buffers);
|
||||
|
||||
region->protect();
|
||||
|
||||
return *result;
|
||||
}
|
||||
|
||||
protected_region *texture_cache::find_region(u32 address)
|
||||
{
|
||||
//TODO
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
protected_region *texture_cache::find_region(u32 address, u32 size)
|
||||
{
|
||||
//TODO
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void texture_cache::clear()
|
||||
{
|
||||
for (auto& entry : m_protected_regions)
|
||||
{
|
||||
entry.second.clear();
|
||||
}
|
||||
|
||||
m_protected_regions.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#include <vector>
|
||||
#include "Utilities/types.h"
|
||||
#include "gl_helpers.h"
|
||||
#include "rsx_gl_texture.h"
|
||||
|
||||
namespace gl
|
||||
{
|
||||
|
@ -19,94 +18,133 @@ namespace gl
|
|||
none = 0,
|
||||
host = 1 << 0,
|
||||
local = 1 << 1,
|
||||
all = host | local,
|
||||
};
|
||||
|
||||
enum class cache_entry_state
|
||||
{
|
||||
invalidated = 0,
|
||||
invalid = 0,
|
||||
local_synchronized = 1 << 0,
|
||||
host_synchronized = 1 << 1,
|
||||
synchronized = local_synchronized | host_synchronized,
|
||||
};
|
||||
|
||||
enum class texture_flags
|
||||
{
|
||||
none = 0,
|
||||
swap_bytes = 1 << 0,
|
||||
allow_remap = 1 << 1,
|
||||
allow_swizzle = 1 << 2
|
||||
};
|
||||
|
||||
DECLARE_ENUM_CLASS_BITWISE_OPERATORS(cache_access);
|
||||
DECLARE_ENUM_CLASS_BITWISE_OPERATORS(cache_buffers);
|
||||
DECLARE_ENUM_CLASS_BITWISE_OPERATORS(cache_entry_state);
|
||||
DECLARE_ENUM_CLASS_BITWISE_OPERATORS(texture_flags);
|
||||
|
||||
struct texture_format
|
||||
{
|
||||
u8 bpp;
|
||||
const GLint *remap;
|
||||
texture::internal_format internal_format;
|
||||
texture::format format;
|
||||
texture::type type;
|
||||
texture_flags flags;
|
||||
};
|
||||
|
||||
struct texture_info
|
||||
{
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 depth;
|
||||
u32 pitch;
|
||||
u32 dimension;
|
||||
u32 compressed_size;
|
||||
|
||||
texture::target target;
|
||||
texture_format format;
|
||||
|
||||
u32 start_address;
|
||||
|
||||
u32 size() const
|
||||
{
|
||||
return height * pitch * depth;
|
||||
}
|
||||
};
|
||||
|
||||
struct protected_region;
|
||||
|
||||
struct cached_texture
|
||||
{
|
||||
u32 gl_id;
|
||||
u32 w;
|
||||
u32 h;
|
||||
u64 data_addr;
|
||||
u32 block_sz;
|
||||
u32 frame_ctr;
|
||||
u32 protected_block_start;
|
||||
u32 protected_block_sz;
|
||||
u16 mipmap;
|
||||
bool deleted;
|
||||
bool locked;
|
||||
texture_info info;
|
||||
GLuint gl_name = 0;
|
||||
|
||||
private:
|
||||
cache_entry_state m_state = cache_entry_state::host_synchronized;
|
||||
protected_region *m_parent_region = nullptr;
|
||||
|
||||
private:
|
||||
void read();
|
||||
void write();
|
||||
|
||||
public:
|
||||
//Can be called from RSX thread only
|
||||
bool sync(cache_buffers buffers);
|
||||
void invalidate(cache_buffers buffers);
|
||||
void ignore(cache_buffers buffers);
|
||||
void parent(protected_region *region);
|
||||
bool is_synchronized(cache_buffers buffers) const;
|
||||
|
||||
void bind(uint index = ~0u) const;
|
||||
|
||||
protected:
|
||||
void create();
|
||||
void remove();
|
||||
bool created() const;
|
||||
|
||||
friend protected_region;
|
||||
};
|
||||
|
||||
struct invalid_cache_area
|
||||
struct protected_region
|
||||
{
|
||||
u32 block_base;
|
||||
u32 block_sz;
|
||||
};
|
||||
u32 start_address;
|
||||
u32 pages_count;
|
||||
|
||||
struct cached_rtt
|
||||
{
|
||||
bool valid = false;
|
||||
bool locked;
|
||||
bool is_dirty;
|
||||
bool is_depth;
|
||||
u32 copy_glid;
|
||||
u32 data_addr;
|
||||
u32 block_sz;
|
||||
u32 current_width;
|
||||
u32 current_height;
|
||||
private:
|
||||
std::map<u32, cached_texture> m_textures;
|
||||
|
||||
u32 m_read_protected = 0;
|
||||
u32 m_write_protected = 0;
|
||||
u32 m_current_protection = 0;
|
||||
|
||||
public:
|
||||
u32 size() const
|
||||
{
|
||||
return pages_count * vm::page_size;
|
||||
}
|
||||
|
||||
void for_each(std::function<void(cached_texture& texture)> callback);
|
||||
void for_each(u32 start_address, u32 size, std::function<void(cached_texture& texture)> callback);
|
||||
void protect();
|
||||
void unprotect(cache_access access = cache_access::read_write);
|
||||
bool empty() const;
|
||||
|
||||
void separate(protected_region& dst);
|
||||
void combine(const protected_region& region);
|
||||
|
||||
void clear();
|
||||
|
||||
friend cached_texture;
|
||||
};
|
||||
|
||||
class texture_cache
|
||||
{
|
||||
std::vector<cached_texture> m_texture_cache;
|
||||
std::vector<cached_rtt> m_rtt_cache;
|
||||
u32 m_frame_ctr = 0;
|
||||
std::map<u32, protected_region> m_protected_regions;
|
||||
|
||||
public:
|
||||
~texture_cache();
|
||||
|
||||
private:
|
||||
bool lock_memory_region(u32 start, u32 size);
|
||||
bool unlock_memory_region(u32 start, u32 size);
|
||||
void lock_gl_object(cached_texture &obj);
|
||||
void unlock_gl_object(cached_texture &obj);
|
||||
|
||||
cached_texture *find_obj_for_params(u64 texaddr, u32 w, u32 h, u16 mipmap);
|
||||
cached_texture& create_obj_for_params(u32 gl_id, u64 texaddr, u32 w, u32 h, u16 mipmap);
|
||||
|
||||
void remove_obj(cached_texture &tex);
|
||||
void remove_obj_for_glid(u32 gl_id);
|
||||
void clear_obj_cache();
|
||||
bool region_overlaps(u32 base1, u32 limit1, u32 base2, u32 limit2);
|
||||
cached_rtt* find_cached_rtt(u32 base, u32 size);
|
||||
void invalidate_rtts_in_range(u32 base, u32 size);
|
||||
void prep_rtt(cached_rtt &rtt, u32 width, u32 height, u32 gl_pixel_format_internal);
|
||||
void save_rtt(u32 base, u32 size, u32 width, u32 height, u32 gl_pixel_format_internal, gl::texture &source);
|
||||
void write_rtt(u32 base, u32 size, u32 texaddr);
|
||||
void destroy_rtt_cache();
|
||||
|
||||
public:
|
||||
void update_frame_ctr();
|
||||
void initialize_rtt_cache();
|
||||
void upload_texture(int index, rsx::texture &tex, rsx::gl::texture &gl_texture);
|
||||
bool mark_local_as_dirty_at(u32 address);
|
||||
void save_render_target(u32 texaddr, u32 range, gl::texture &gl_texture);
|
||||
std::vector<invalid_cache_area> find_and_invalidate_in_range(u32 base, u32 limit);
|
||||
void lock_invalidated_ranges(const std::vector<invalid_cache_area> &invalid);
|
||||
void remove_in_range(u32 texaddr, u32 range);
|
||||
bool explicit_writeback(gl::texture &tex, const u32 address, const u32 pitch);
|
||||
|
||||
bool sync_at(cache_buffers buffer, u32 address);
|
||||
cached_texture &entry(texture_info &info, cache_buffers sync_buffers = cache_buffers::none);
|
||||
protected_region *find_region(u32 address);
|
||||
protected_region *find_region(u32 address, u32 size);
|
||||
void clear();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,474 +6,241 @@
|
|||
#include "../RSXTexture.h"
|
||||
#include "../rsx_utils.h"
|
||||
#include "../Common/TextureUtils.h"
|
||||
#include "gl_texture_cache.h"
|
||||
|
||||
namespace rsx
|
||||
const GLint default_remap[4]{ GL_ALPHA, GL_RED, GL_GREEN, GL_BLUE };
|
||||
const GLint remap_B8[4]{ GL_BLUE, GL_BLUE, GL_BLUE, GL_BLUE };
|
||||
const GLint remap_G8B8[4]{ GL_RED, GL_GREEN, GL_RED, GL_GREEN };
|
||||
const GLint remap_R6G5B5[4]{ GL_ALPHA, GL_GREEN, GL_RED, GL_BLUE };
|
||||
const GLint remap_X16[4]{ GL_RED, GL_ONE, GL_RED, GL_ONE };
|
||||
const GLint remap_Y16_X16[4]{ GL_GREEN, GL_RED, GL_GREEN, GL_RED };
|
||||
const GLint remap_FLOAT[4]{ GL_RED, GL_ONE, GL_ONE, GL_ONE };
|
||||
const GLint remap_X32_FLOAT[4]{ GL_RED, GL_ONE, GL_ONE, GL_ONE };
|
||||
const GLint remap_D1R5G5B5[4]{ GL_ONE, GL_RED, GL_GREEN, GL_BLUE };
|
||||
const GLint remap_D8R8G8B8[4]{ GL_ONE, GL_RED, GL_GREEN, GL_BLUE };
|
||||
const GLint remap_Y16_X16_FLOAT[4]{ GL_RED, GL_GREEN, GL_RED, GL_GREEN };
|
||||
|
||||
const std::unordered_map<u32, gl::texture_format> textures_fromats
|
||||
{
|
||||
namespace gl
|
||||
{ CELL_GCM_TEXTURE_B8,{ 1, remap_B8, gl::texture::internal_format::rgba, gl::texture::format::blue, gl::texture::type::ubyte, gl::texture_flags::none } },
|
||||
{ CELL_GCM_TEXTURE_A1R5G5B5,{ 2, default_remap, gl::texture::internal_format::rgba, gl::texture::format::bgra, gl::texture::type::ushort_5_5_5_1, gl::texture_flags::allow_remap | gl::texture_flags::allow_swizzle } },
|
||||
{ CELL_GCM_TEXTURE_A4R4G4B4,{ 2, default_remap, gl::texture::internal_format::rgba, gl::texture::format::bgra, gl::texture::type::ushort_4_4_4_4, gl::texture_flags::allow_remap | gl::texture_flags::allow_swizzle } },
|
||||
{ CELL_GCM_TEXTURE_R5G6B5,{ 2, default_remap, gl::texture::internal_format::rgb, gl::texture::format::bgr, gl::texture::type::ushort_5_6_5, gl::texture_flags::allow_remap | gl::texture_flags::allow_swizzle } },
|
||||
{ CELL_GCM_TEXTURE_A8R8G8B8,{ 4, default_remap, gl::texture::internal_format::rgba, gl::texture::format::bgra, gl::texture::type::uint_8_8_8_8, gl::texture_flags::allow_remap | gl::texture_flags::allow_swizzle } },
|
||||
{ CELL_GCM_TEXTURE_G8B8,{ 2, remap_G8B8, gl::texture::internal_format::rgba, gl::texture::format::rg, gl::texture::type::ubyte, gl::texture_flags::allow_remap } },
|
||||
{ CELL_GCM_TEXTURE_R6G5B5,{ 2, remap_R6G5B5, gl::texture::internal_format::rgba, gl::texture::format::bgr, gl::texture::type::ushort_5_6_5, gl::texture_flags::allow_remap | gl::texture_flags::allow_swizzle } },
|
||||
{ CELL_GCM_TEXTURE_DEPTH24_D8,{ 4, default_remap, gl::texture::internal_format::depth24, gl::texture::format::depth, gl::texture::type::uint_8_8_8_8_rev, gl::texture_flags::allow_remap } },
|
||||
{ CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT,{ 4, default_remap, gl::texture::internal_format::depth24, gl::texture::format::depth, gl::texture::type::f32, gl::texture_flags::allow_remap } },
|
||||
{ CELL_GCM_TEXTURE_DEPTH16,{ 2, default_remap, gl::texture::internal_format::depth16, gl::texture::format::depth, gl::texture::type::ushort, gl::texture_flags::allow_remap } },
|
||||
{ CELL_GCM_TEXTURE_DEPTH16_FLOAT,{ 2, default_remap, gl::texture::internal_format::depth16, gl::texture::format::depth, gl::texture::type::f32, gl::texture_flags::allow_remap } },
|
||||
{ CELL_GCM_TEXTURE_X16,{ 2, remap_X16, gl::texture::internal_format::rgba, gl::texture::format::red, gl::texture::type::ushort, gl::texture_flags::swap_bytes } },
|
||||
{ CELL_GCM_TEXTURE_Y16_X16,{ 4, remap_Y16_X16, gl::texture::internal_format::rgba, gl::texture::format::rg, gl::texture::type::ushort, gl::texture_flags::allow_remap | gl::texture_flags::swap_bytes } },
|
||||
{ CELL_GCM_TEXTURE_R5G5B5A1,{ 2, default_remap, gl::texture::internal_format::rgba, gl::texture::format::rgba, gl::texture::type::ushort_5_5_5_1, gl::texture_flags::allow_remap | gl::texture_flags::swap_bytes | gl::texture_flags::allow_swizzle } },
|
||||
{ CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT,{ 8, default_remap, gl::texture::internal_format::rgba, gl::texture::format::rgba, gl::texture::type::f16, gl::texture_flags::allow_remap | gl::texture_flags::swap_bytes } },
|
||||
{ CELL_GCM_TEXTURE_W32_Z32_Y32_X32_FLOAT,{ 16, default_remap, gl::texture::internal_format::rgba, gl::texture::format::rgba, gl::texture::type::f32, gl::texture_flags::allow_remap | gl::texture_flags::swap_bytes } },
|
||||
{ CELL_GCM_TEXTURE_X32_FLOAT,{ 4, remap_X32_FLOAT, gl::texture::internal_format::rgba, gl::texture::format::red, gl::texture::type::f32, gl::texture_flags::swap_bytes } },
|
||||
{ CELL_GCM_TEXTURE_D1R5G5B5,{ 2, remap_D1R5G5B5, gl::texture::internal_format::rgba, gl::texture::format::bgra, gl::texture::type::ushort_5_5_5_1, gl::texture_flags::allow_remap | gl::texture_flags::allow_swizzle } },
|
||||
{ CELL_GCM_TEXTURE_D8R8G8B8,{ 4, remap_D8R8G8B8, gl::texture::internal_format::rgba, gl::texture::format::bgra, gl::texture::type::uint_8_8_8_8, gl::texture_flags::allow_remap | gl::texture_flags::allow_swizzle } },
|
||||
{ CELL_GCM_TEXTURE_Y16_X16_FLOAT,{ 4, remap_Y16_X16_FLOAT, gl::texture::internal_format::rgba, gl::texture::format::rg, gl::texture::type::f16, gl::texture_flags::allow_remap | gl::texture_flags::swap_bytes } },
|
||||
};
|
||||
|
||||
static const int gl_tex_min_filter[] =
|
||||
{
|
||||
GL_NEAREST, // unused
|
||||
GL_NEAREST,
|
||||
GL_LINEAR,
|
||||
GL_NEAREST_MIPMAP_NEAREST,
|
||||
GL_LINEAR_MIPMAP_NEAREST,
|
||||
GL_NEAREST_MIPMAP_LINEAR,
|
||||
GL_LINEAR_MIPMAP_LINEAR,
|
||||
GL_NEAREST, // CELL_GCM_TEXTURE_CONVOLUTION_MIN
|
||||
};
|
||||
|
||||
static const int gl_tex_mag_filter[] =
|
||||
{
|
||||
GL_NEAREST, // unused
|
||||
GL_NEAREST,
|
||||
GL_LINEAR,
|
||||
GL_NEAREST, // unused
|
||||
GL_LINEAR // CELL_GCM_TEXTURE_CONVOLUTION_MAG
|
||||
};
|
||||
|
||||
static const int gl_tex_zfunc[] =
|
||||
{
|
||||
GL_NEVER,
|
||||
GL_LESS,
|
||||
GL_EQUAL,
|
||||
GL_LEQUAL,
|
||||
GL_GREATER,
|
||||
GL_NOTEQUAL,
|
||||
GL_GEQUAL,
|
||||
GL_ALWAYS,
|
||||
};
|
||||
|
||||
float max_aniso(int aniso)
|
||||
{
|
||||
switch (aniso)
|
||||
{
|
||||
static const int gl_tex_min_filter[] =
|
||||
case CELL_GCM_TEXTURE_MAX_ANISO_1: return 1.0f;
|
||||
case CELL_GCM_TEXTURE_MAX_ANISO_2: return 2.0f;
|
||||
case CELL_GCM_TEXTURE_MAX_ANISO_4: return 4.0f;
|
||||
case CELL_GCM_TEXTURE_MAX_ANISO_6: return 6.0f;
|
||||
case CELL_GCM_TEXTURE_MAX_ANISO_8: return 8.0f;
|
||||
case CELL_GCM_TEXTURE_MAX_ANISO_10: return 10.0f;
|
||||
case CELL_GCM_TEXTURE_MAX_ANISO_12: return 12.0f;
|
||||
case CELL_GCM_TEXTURE_MAX_ANISO_16: return 16.0f;
|
||||
}
|
||||
|
||||
LOG_ERROR(RSX, "Texture anisotropy error: bad max aniso (%d).", aniso);
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
|
||||
bool compressed_format(u32 format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_DXT1:
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_DXT23:
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_DXT45:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int wrap(int wrap)
|
||||
{
|
||||
switch (wrap)
|
||||
{
|
||||
case CELL_GCM_TEXTURE_WRAP: return GL_REPEAT;
|
||||
case CELL_GCM_TEXTURE_MIRROR: return GL_MIRRORED_REPEAT;
|
||||
case CELL_GCM_TEXTURE_CLAMP_TO_EDGE: return GL_CLAMP_TO_EDGE;
|
||||
case CELL_GCM_TEXTURE_BORDER: return GL_CLAMP_TO_BORDER;
|
||||
case CELL_GCM_TEXTURE_CLAMP: return GL_CLAMP;
|
||||
case CELL_GCM_TEXTURE_MIRROR_ONCE_CLAMP_TO_EDGE: return GL_MIRROR_CLAMP_TO_EDGE_EXT;
|
||||
case CELL_GCM_TEXTURE_MIRROR_ONCE_BORDER: return GL_MIRROR_CLAMP_TO_BORDER_EXT;
|
||||
case CELL_GCM_TEXTURE_MIRROR_ONCE_CLAMP: return GL_MIRROR_CLAMP_EXT;
|
||||
}
|
||||
|
||||
LOG_ERROR(RSX, "Texture wrap error: bad wrap (%d).", wrap);
|
||||
return GL_REPEAT;
|
||||
}
|
||||
|
||||
|
||||
void rsx::gl_texture::bind(gl::texture_cache& cache, int index, rsx::texture& tex)
|
||||
{
|
||||
u32 full_format = tex.format();
|
||||
u32 format = full_format & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN);
|
||||
bool is_swizzled = (~full_format & CELL_GCM_TEXTURE_LN) != 0;
|
||||
bool is_normalized = (~full_format & CELL_GCM_TEXTURE_UN) != 0;
|
||||
|
||||
gl::texture::target target = is_normalized ? gl::texture::target::texture2D : gl::texture::target::texture_rectangle;
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + index);
|
||||
gl::texture_view(target, 0).bind();
|
||||
|
||||
if (!tex.enabled())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool is_compressed = compressed_format(full_format);
|
||||
|
||||
const GLint *remap = default_remap;
|
||||
|
||||
//TODO
|
||||
gl::texture_info info{};
|
||||
|
||||
info.width = std::max<u16>(tex.width(), 1);
|
||||
info.height = std::max<u16>(tex.height(), 1);
|
||||
info.depth = std::max<u16>(tex.depth(), 1);
|
||||
info.dimension = tex.dimension();
|
||||
|
||||
if (is_compressed)
|
||||
{
|
||||
info.format.type = gl::texture::type::ubyte;
|
||||
info.format.format = gl::texture::format::rgba;
|
||||
info.pitch = 0;
|
||||
|
||||
switch (format)
|
||||
{
|
||||
GL_NEAREST, // unused
|
||||
GL_NEAREST,
|
||||
GL_LINEAR,
|
||||
GL_NEAREST_MIPMAP_NEAREST,
|
||||
GL_LINEAR_MIPMAP_NEAREST,
|
||||
GL_NEAREST_MIPMAP_LINEAR,
|
||||
GL_LINEAR_MIPMAP_LINEAR,
|
||||
GL_NEAREST, // CELL_GCM_TEXTURE_CONVOLUTION_MIN
|
||||
};
|
||||
|
||||
static const int gl_tex_mag_filter[] =
|
||||
{
|
||||
GL_NEAREST, // unused
|
||||
GL_NEAREST,
|
||||
GL_LINEAR,
|
||||
GL_NEAREST, // unused
|
||||
GL_LINEAR // CELL_GCM_TEXTURE_CONVOLUTION_MAG
|
||||
};
|
||||
|
||||
static const int gl_tex_zfunc[] =
|
||||
{
|
||||
GL_NEVER,
|
||||
GL_LESS,
|
||||
GL_EQUAL,
|
||||
GL_LEQUAL,
|
||||
GL_GREATER,
|
||||
GL_NOTEQUAL,
|
||||
GL_GEQUAL,
|
||||
GL_ALWAYS,
|
||||
};
|
||||
|
||||
void texture::create()
|
||||
{
|
||||
if (m_id)
|
||||
{
|
||||
remove();
|
||||
}
|
||||
|
||||
glGenTextures(1, &m_id);
|
||||
}
|
||||
|
||||
int texture::gl_wrap(int wrap)
|
||||
{
|
||||
switch (wrap)
|
||||
{
|
||||
case CELL_GCM_TEXTURE_WRAP: return GL_REPEAT;
|
||||
case CELL_GCM_TEXTURE_MIRROR: return GL_MIRRORED_REPEAT;
|
||||
case CELL_GCM_TEXTURE_CLAMP_TO_EDGE: return GL_CLAMP_TO_EDGE;
|
||||
case CELL_GCM_TEXTURE_BORDER: return GL_CLAMP_TO_BORDER;
|
||||
case CELL_GCM_TEXTURE_CLAMP: return GL_CLAMP;
|
||||
case CELL_GCM_TEXTURE_MIRROR_ONCE_CLAMP_TO_EDGE: return GL_MIRROR_CLAMP_TO_EDGE_EXT;
|
||||
case CELL_GCM_TEXTURE_MIRROR_ONCE_BORDER: return GL_MIRROR_CLAMP_TO_BORDER_EXT;
|
||||
case CELL_GCM_TEXTURE_MIRROR_ONCE_CLAMP: return GL_MIRROR_CLAMP_EXT;
|
||||
}
|
||||
|
||||
LOG_ERROR(RSX, "Texture wrap error: bad wrap (%d).", wrap);
|
||||
return GL_REPEAT;
|
||||
}
|
||||
|
||||
float texture::max_aniso(int aniso)
|
||||
{
|
||||
switch (aniso)
|
||||
{
|
||||
case CELL_GCM_TEXTURE_MAX_ANISO_1: return 1.0f;
|
||||
case CELL_GCM_TEXTURE_MAX_ANISO_2: return 2.0f;
|
||||
case CELL_GCM_TEXTURE_MAX_ANISO_4: return 4.0f;
|
||||
case CELL_GCM_TEXTURE_MAX_ANISO_6: return 6.0f;
|
||||
case CELL_GCM_TEXTURE_MAX_ANISO_8: return 8.0f;
|
||||
case CELL_GCM_TEXTURE_MAX_ANISO_10: return 10.0f;
|
||||
case CELL_GCM_TEXTURE_MAX_ANISO_12: return 12.0f;
|
||||
case CELL_GCM_TEXTURE_MAX_ANISO_16: return 16.0f;
|
||||
}
|
||||
|
||||
LOG_ERROR(RSX, "Texture anisotropy error: bad max aniso (%d).", aniso);
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
u16 texture::get_pitch_modifier(u32 format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_HILO8:
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_HILO_S8:
|
||||
default:
|
||||
LOG_ERROR(RSX, "Unimplemented Texture format : 0x%x", format);
|
||||
return 0;
|
||||
case ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN) & CELL_GCM_TEXTURE_COMPRESSED_B8R8_G8R8:
|
||||
case ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN) & CELL_GCM_TEXTURE_COMPRESSED_R8B8_R8G8:
|
||||
return 4;
|
||||
case CELL_GCM_TEXTURE_B8:
|
||||
return 1;
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_DXT1:
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_DXT23:
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_DXT45:
|
||||
return 0;
|
||||
case CELL_GCM_TEXTURE_A1R5G5B5:
|
||||
case CELL_GCM_TEXTURE_A4R4G4B4:
|
||||
case CELL_GCM_TEXTURE_R5G6B5:
|
||||
case CELL_GCM_TEXTURE_G8B8:
|
||||
case CELL_GCM_TEXTURE_R6G5B5:
|
||||
case CELL_GCM_TEXTURE_DEPTH16:
|
||||
case CELL_GCM_TEXTURE_DEPTH16_FLOAT:
|
||||
case CELL_GCM_TEXTURE_X16:
|
||||
case CELL_GCM_TEXTURE_R5G5B5A1:
|
||||
case CELL_GCM_TEXTURE_D1R5G5B5:
|
||||
return 2;
|
||||
case CELL_GCM_TEXTURE_A8R8G8B8:
|
||||
case CELL_GCM_TEXTURE_X32_FLOAT:
|
||||
case CELL_GCM_TEXTURE_Y16_X16_FLOAT:
|
||||
case CELL_GCM_TEXTURE_D8R8G8B8:
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_B8R8_G8R8:
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_R8B8_R8G8:
|
||||
case CELL_GCM_TEXTURE_DEPTH24_D8:
|
||||
case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT:
|
||||
case CELL_GCM_TEXTURE_Y16_X16:
|
||||
return 4;
|
||||
case CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT:
|
||||
return 8;
|
||||
case CELL_GCM_TEXTURE_W32_Z32_Y32_X32_FLOAT:
|
||||
return 16;
|
||||
}
|
||||
}
|
||||
|
||||
bool texture::mandates_expansion(u32 format)
|
||||
{
|
||||
/**
|
||||
* If a texture behaves differently when uploaded directly vs when uploaded via texutils methods, it should be added here.
|
||||
*/
|
||||
if (format == CELL_GCM_TEXTURE_A1R5G5B5)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void texture::init(int index, rsx::texture& tex)
|
||||
{
|
||||
if (!m_id)
|
||||
{
|
||||
create();
|
||||
}
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + index);
|
||||
bind();
|
||||
|
||||
const u32 texaddr = rsx::get_address(tex.offset(), tex.location());
|
||||
//LOG_WARNING(RSX, "texture addr = 0x%x, width = %d, height = %d, max_aniso=%d, mipmap=%d, remap=0x%x, zfunc=0x%x, wraps=0x%x, wrapt=0x%x, wrapr=0x%x, minlod=0x%x, maxlod=0x%x",
|
||||
// m_offset, m_width, m_height, m_maxaniso, m_mipmap, m_remap, m_zfunc, m_wraps, m_wrapt, m_wrapr, m_minlod, m_maxlod);
|
||||
|
||||
//TODO: safe init
|
||||
|
||||
u32 full_format = tex.format();
|
||||
|
||||
u32 format = full_format & ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN);
|
||||
bool is_swizzled = !!(~full_format & CELL_GCM_TEXTURE_LN);
|
||||
|
||||
static const GLint glRemapStandard[4] = { GL_ALPHA, GL_RED, GL_GREEN, GL_BLUE };
|
||||
// NOTE: This must be in ARGB order in all forms below.
|
||||
const GLint *glRemap = glRemapStandard;
|
||||
|
||||
::gl::pixel_pack_settings().apply();
|
||||
::gl::pixel_unpack_settings().apply();
|
||||
|
||||
u32 aligned_pitch = tex.pitch();
|
||||
|
||||
size_t texture_data_sz = get_placed_texture_storage_size(tex, 256);
|
||||
std::vector<u8> data_upload_buf(texture_data_sz);
|
||||
u8* texture_data = data_upload_buf.data();
|
||||
u32 block_sz = get_pitch_modifier(format);
|
||||
|
||||
if (is_swizzled || mandates_expansion(format))
|
||||
{
|
||||
aligned_pitch = align(aligned_pitch, 256);
|
||||
upload_placed_texture({ reinterpret_cast<gsl::byte*>(texture_data), gsl::narrow<int>(texture_data_sz) }, tex, 256);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
texture_data = vm::ps3::_ptr<u8>(texaddr);
|
||||
}
|
||||
|
||||
if (block_sz)
|
||||
aligned_pitch /= block_sz;
|
||||
else
|
||||
aligned_pitch = 0;
|
||||
|
||||
glPixelStorei(GL_UNPACK_ROW_LENGTH, aligned_pitch);
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case CELL_GCM_TEXTURE_B8: // One 8-bit fixed-point number
|
||||
{
|
||||
glTexImage2D(m_target, 0, GL_RGBA, tex.width(), tex.height(), 0, GL_BLUE, GL_UNSIGNED_BYTE, texture_data);
|
||||
|
||||
static const GLint swizzleMaskB8[] = { GL_BLUE, GL_BLUE, GL_BLUE, GL_BLUE };
|
||||
glRemap = swizzleMaskB8;
|
||||
break;
|
||||
}
|
||||
|
||||
case CELL_GCM_TEXTURE_A1R5G5B5:
|
||||
{
|
||||
glTexImage2D(m_target, 0, GL_RGBA, tex.width(), tex.height(), 0, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, texture_data);
|
||||
break;
|
||||
}
|
||||
|
||||
case CELL_GCM_TEXTURE_A4R4G4B4:
|
||||
{
|
||||
glTexImage2D(m_target, 0, GL_RGBA, tex.width(), tex.height(), 0, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4, texture_data);
|
||||
|
||||
// We read it in as R4G4B4A4, so we need to remap each component.
|
||||
static const GLint swizzleMaskA4R4G4B4[] = { GL_BLUE, GL_ALPHA, GL_RED, GL_GREEN };
|
||||
glRemap = swizzleMaskA4R4G4B4;
|
||||
break;
|
||||
}
|
||||
|
||||
case CELL_GCM_TEXTURE_R5G6B5:
|
||||
{
|
||||
LOG_WARNING(RSX, "CELL_GCM_R5G6B5 texture. Watch out for corruption due to swapped color channels!");
|
||||
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE);
|
||||
glTexImage2D(m_target, 0, GL_RGB, tex.width(), tex.height(), 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, texture_data);
|
||||
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
|
||||
break;
|
||||
}
|
||||
|
||||
case CELL_GCM_TEXTURE_A8R8G8B8:
|
||||
{
|
||||
glTexImage2D(m_target, 0, GL_RGBA, tex.width(), tex.height(), 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, texture_data);
|
||||
break;
|
||||
}
|
||||
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_DXT1: // Compressed 4x4 pixels into 8 bytes
|
||||
{
|
||||
u32 size = ((tex.width() + 3) / 4) * ((tex.height() + 3) / 4) * 8;
|
||||
glCompressedTexImage2D(m_target, 0, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, tex.width(), tex.height(), 0, size, texture_data);
|
||||
break;
|
||||
}
|
||||
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_DXT23: // Compressed 4x4 pixels into 16 bytes
|
||||
{
|
||||
u32 size = ((tex.width() + 3) / 4) * ((tex.height() + 3) / 4) * 16;
|
||||
glCompressedTexImage2D(m_target, 0, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, tex.width(), tex.height(), 0, size, texture_data);
|
||||
}
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_DXT1: // Compressed 4x4 pixels into 8 bytes
|
||||
info.compressed_size = ((info.width + 3) / 4) * ((info.height + 3) / 4) * 8;
|
||||
info.format.internal_format = gl::texture::internal_format::compressed_rgba_s3tc_dxt1;
|
||||
break;
|
||||
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_DXT45: // Compressed 4x4 pixels into 16 bytes
|
||||
{
|
||||
u32 size = ((tex.width() + 3) / 4) * ((tex.height() + 3) / 4) * 16;
|
||||
glCompressedTexImage2D(m_target, 0, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, tex.width(), tex.height(), 0, size, texture_data);
|
||||
break;
|
||||
}
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_DXT23: // Compressed 4x4 pixels into 16 bytes
|
||||
info.compressed_size = ((info.width + 3) / 4) * ((info.height + 3) / 4) * 16;
|
||||
info.format.internal_format = gl::texture::internal_format::compressed_rgba_s3tc_dxt3;
|
||||
break;
|
||||
|
||||
case CELL_GCM_TEXTURE_G8B8:
|
||||
{
|
||||
glTexImage2D(m_target, 0, GL_RGBA, tex.width(), tex.height(), 0, GL_RG, GL_UNSIGNED_BYTE, texture_data);
|
||||
case CELL_GCM_TEXTURE_COMPRESSED_DXT45: // Compressed 4x4 pixels into 16 bytes
|
||||
info.compressed_size = ((info.width + 3) / 4) * ((info.height + 3) / 4) * 16;
|
||||
info.format.internal_format = gl::texture::internal_format::compressed_rgba_s3tc_dxt5;
|
||||
break;
|
||||
|
||||
static const GLint swizzleMaskG8B8[] = { GL_RED, GL_GREEN, GL_RED, GL_GREEN };
|
||||
glRemap = swizzleMaskG8B8;
|
||||
break;
|
||||
}
|
||||
|
||||
case CELL_GCM_TEXTURE_R6G5B5:
|
||||
{
|
||||
glTexImage2D(m_target, 0, GL_RGBA, tex.width(), tex.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, texture_data);
|
||||
break;
|
||||
}
|
||||
|
||||
case CELL_GCM_TEXTURE_DEPTH24_D8: // 24-bit unsigned fixed-point number and 8 bits of garbage
|
||||
{
|
||||
glTexImage2D(m_target, 0, GL_DEPTH_COMPONENT24, tex.width(), tex.height(), 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, texture_data);
|
||||
break;
|
||||
}
|
||||
|
||||
case CELL_GCM_TEXTURE_DEPTH24_D8_FLOAT: // 24-bit unsigned float and 8 bits of garbage
|
||||
{
|
||||
glTexImage2D(m_target, 0, GL_DEPTH_COMPONENT24, tex.width(), tex.height(), 0, GL_DEPTH_COMPONENT, GL_FLOAT, texture_data);
|
||||
break;
|
||||
}
|
||||
|
||||
case CELL_GCM_TEXTURE_DEPTH16: // 16-bit unsigned fixed-point number
|
||||
{
|
||||
glTexImage2D(m_target, 0, GL_DEPTH_COMPONENT16, tex.width(), tex.height(), 0, GL_DEPTH_COMPONENT, GL_SHORT, texture_data);
|
||||
break;
|
||||
}
|
||||
|
||||
case CELL_GCM_TEXTURE_DEPTH16_FLOAT: // 16-bit unsigned float
|
||||
{
|
||||
glTexImage2D(m_target, 0, GL_DEPTH_COMPONENT16, tex.width(), tex.height(), 0, GL_DEPTH_COMPONENT, GL_FLOAT, texture_data);
|
||||
break;
|
||||
}
|
||||
|
||||
case CELL_GCM_TEXTURE_X16: // A 16-bit fixed-point number
|
||||
{
|
||||
LOG_WARNING(RSX, "CELL_GCM_X16 texture. Watch out for corruption due to swapped color channels!");
|
||||
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE);
|
||||
glTexImage2D(m_target, 0, GL_RGBA, tex.width(), tex.height(), 0, GL_RED, GL_UNSIGNED_SHORT, texture_data);
|
||||
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
|
||||
|
||||
static const GLint swizzleMaskX16[] = { GL_RED, GL_ONE, GL_RED, GL_ONE };
|
||||
glRemap = swizzleMaskX16;
|
||||
break;
|
||||
}
|
||||
|
||||
case CELL_GCM_TEXTURE_Y16_X16: // Two 16-bit fixed-point numbers
|
||||
{
|
||||
LOG_WARNING(RSX, "CELL_GCM_Y16_X16 texture. Watch out for corruption due to swapped color channels!");
|
||||
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE);
|
||||
glTexImage2D(m_target, 0, GL_RGBA, tex.width(), tex.height(), 0, GL_RG, GL_UNSIGNED_SHORT, texture_data);
|
||||
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
|
||||
|
||||
static const GLint swizzleMaskX32_Y16_X16[] = { GL_GREEN, GL_RED, GL_GREEN, GL_RED };
|
||||
glRemap = swizzleMaskX32_Y16_X16;
|
||||
break;
|
||||
}
|
||||
|
||||
case CELL_GCM_TEXTURE_R5G5B5A1:
|
||||
{
|
||||
LOG_WARNING(RSX, "CELL_GCM_R5G6B5A1 texture. Watch out for corruption due to swapped color channels!");
|
||||
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE);
|
||||
glTexImage2D(m_target, 0, GL_RGBA, tex.width(), tex.height(), 0, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1, texture_data);
|
||||
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
|
||||
break;
|
||||
}
|
||||
|
||||
case CELL_GCM_TEXTURE_W16_Z16_Y16_X16_FLOAT: // Four fp16 values
|
||||
{
|
||||
LOG_WARNING(RSX, "CELL_GCM_W16_Z16_Y16_X16_FLOAT texture. Watch out for corruption due to swapped color channels!");
|
||||
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE);
|
||||
glTexImage2D(m_target, 0, GL_RGBA, tex.width(), tex.height(), 0, GL_RGBA, GL_HALF_FLOAT, texture_data);
|
||||
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
|
||||
break;
|
||||
}
|
||||
|
||||
case CELL_GCM_TEXTURE_W32_Z32_Y32_X32_FLOAT: // Four fp32 values
|
||||
{
|
||||
LOG_WARNING(RSX, "CELL_GCM_W32_Z32_Y32_X32_FLOAT texture. Watch out for corruption due to swapped color channels!");
|
||||
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE);
|
||||
glTexImage2D(m_target, 0, GL_RGBA, tex.width(), tex.height(), 0, GL_RGBA, GL_FLOAT, texture_data);
|
||||
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
|
||||
break;
|
||||
}
|
||||
|
||||
case CELL_GCM_TEXTURE_X32_FLOAT: // One 32-bit floating-point number
|
||||
{
|
||||
glTexImage2D(m_target, 0, GL_RGBA, tex.width(), tex.height(), 0, GL_RED, GL_FLOAT, texture_data);
|
||||
|
||||
static const GLint swizzleMaskX32_FLOAT[] = { GL_RED, GL_ONE, GL_ONE, GL_ONE };
|
||||
glRemap = swizzleMaskX32_FLOAT;
|
||||
break;
|
||||
}
|
||||
|
||||
case CELL_GCM_TEXTURE_D1R5G5B5:
|
||||
{
|
||||
LOG_WARNING(RSX, "CELL_GCM_D1R5G5B5 texture. Watch out for corruption due to swapped color channels!");
|
||||
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE);
|
||||
|
||||
// TODO: Texture swizzling
|
||||
glTexImage2D(m_target, 0, GL_RGBA, tex.width(), tex.height(), 0, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, texture_data);
|
||||
|
||||
static const GLint swizzleMaskX32_D1R5G5B5[] = { GL_ONE, GL_RED, GL_GREEN, GL_BLUE };
|
||||
glRemap = swizzleMaskX32_D1R5G5B5;
|
||||
|
||||
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
|
||||
break;
|
||||
}
|
||||
|
||||
case CELL_GCM_TEXTURE_D8R8G8B8: // 8 bits of garbage and three unsigned 8-bit fixed-point numbers
|
||||
{
|
||||
glTexImage2D(m_target, 0, GL_RGBA, tex.width(), tex.height(), 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, texture_data);
|
||||
|
||||
static const GLint swizzleMaskX32_D8R8G8B8[] = { GL_ONE, GL_RED, GL_GREEN, GL_BLUE };
|
||||
glRemap = swizzleMaskX32_D8R8G8B8;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case CELL_GCM_TEXTURE_Y16_X16_FLOAT: // Two fp16 values
|
||||
{
|
||||
LOG_WARNING(RSX, "CELL_GCM_Y16_X16_FLOAT texture. Watch out for corruption due to swapped color channels!");
|
||||
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_TRUE);
|
||||
glTexImage2D(m_target, 0, GL_RGBA, tex.width(), tex.height(), 0, GL_RG, GL_HALF_FLOAT, texture_data);
|
||||
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
|
||||
|
||||
static const GLint swizzleMaskX32_Y16_X16_FLOAT[] = { GL_RED, GL_GREEN, GL_RED, GL_GREEN };
|
||||
glRemap = swizzleMaskX32_Y16_X16_FLOAT;
|
||||
break;
|
||||
}
|
||||
|
||||
case ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN) & CELL_GCM_TEXTURE_COMPRESSED_B8R8_G8R8:
|
||||
case ~(CELL_GCM_TEXTURE_LN | CELL_GCM_TEXTURE_UN) & CELL_GCM_TEXTURE_COMPRESSED_R8B8_R8G8:
|
||||
{
|
||||
glTexImage2D(m_target, 0, GL_RGBA, tex.width(), tex.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, texture_data);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
LOG_ERROR(RSX, "Init tex error: Bad tex format (0x%x | %s | 0x%x)", format, (is_swizzled ? "swizzled" : "linear"), tex.format() & 0x40);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
glTexParameteri(m_target, GL_TEXTURE_MAX_LEVEL, tex.mipmap() - 1);
|
||||
glTexParameteri(m_target, GL_GENERATE_MIPMAP, tex.mipmap() > 1);
|
||||
|
||||
if (format != CELL_GCM_TEXTURE_B8 && format != CELL_GCM_TEXTURE_X16 && format != CELL_GCM_TEXTURE_X32_FLOAT)
|
||||
{
|
||||
u8 remap_a = tex.remap() & 0x3;
|
||||
u8 remap_r = (tex.remap() >> 2) & 0x3;
|
||||
u8 remap_g = (tex.remap() >> 4) & 0x3;
|
||||
u8 remap_b = (tex.remap() >> 6) & 0x3;
|
||||
|
||||
glTexParameteri(m_target, GL_TEXTURE_SWIZZLE_A, glRemap[remap_a]);
|
||||
glTexParameteri(m_target, GL_TEXTURE_SWIZZLE_R, glRemap[remap_r]);
|
||||
glTexParameteri(m_target, GL_TEXTURE_SWIZZLE_G, glRemap[remap_g]);
|
||||
glTexParameteri(m_target, GL_TEXTURE_SWIZZLE_B, glRemap[remap_b]);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
glTexParameteri(m_target, GL_TEXTURE_SWIZZLE_A, glRemap[0]);
|
||||
glTexParameteri(m_target, GL_TEXTURE_SWIZZLE_R, glRemap[1]);
|
||||
glTexParameteri(m_target, GL_TEXTURE_SWIZZLE_G, glRemap[2]);
|
||||
glTexParameteri(m_target, GL_TEXTURE_SWIZZLE_B, glRemap[3]);
|
||||
}
|
||||
|
||||
glTexParameteri(m_target, GL_TEXTURE_WRAP_S, gl_wrap(tex.wrap_s()));
|
||||
glTexParameteri(m_target, GL_TEXTURE_WRAP_T, gl_wrap(tex.wrap_t()));
|
||||
glTexParameteri(m_target, GL_TEXTURE_WRAP_R, gl_wrap(tex.wrap_r()));
|
||||
|
||||
glTexParameteri(m_target, GL_TEXTURE_COMPARE_FUNC, gl_tex_zfunc[tex.zfunc()]);
|
||||
|
||||
glTexEnvi(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, (GLint)tex.bias());
|
||||
glTexParameteri(m_target, GL_TEXTURE_MIN_LOD, (tex.min_lod() >> 8));
|
||||
glTexParameteri(m_target, GL_TEXTURE_MAX_LOD, (tex.max_lod() >> 8));
|
||||
|
||||
glTexParameteri(m_target, GL_TEXTURE_MIN_FILTER, gl_tex_min_filter[tex.min_filter()]);
|
||||
glTexParameteri(m_target, GL_TEXTURE_MAG_FILTER, gl_tex_mag_filter[tex.mag_filter()]);
|
||||
glTexParameterf(m_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_aniso(tex.max_aniso()));
|
||||
}
|
||||
|
||||
void texture::bind()
|
||||
{
|
||||
glBindTexture(m_target, m_id);
|
||||
}
|
||||
|
||||
void texture::unbind()
|
||||
{
|
||||
glBindTexture(m_target, 0);
|
||||
}
|
||||
|
||||
void texture::remove()
|
||||
{
|
||||
if (m_id)
|
||||
{
|
||||
glDeleteTextures(1, &m_id);
|
||||
m_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
u32 texture::id() const
|
||||
{
|
||||
return m_id;
|
||||
default:
|
||||
throw EXCEPTION("bad compressed format");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto found = textures_fromats.find(format);
|
||||
|
||||
if (found == textures_fromats.end())
|
||||
{
|
||||
throw EXCEPTION("unimplemented texture format 0x%x", format);
|
||||
}
|
||||
|
||||
info.format = found->second;
|
||||
info.pitch = tex.pitch();
|
||||
|
||||
if (!info.pitch)
|
||||
{
|
||||
info.pitch = info.width * info.format.bpp;
|
||||
}
|
||||
|
||||
remap = info.format.remap;
|
||||
}
|
||||
|
||||
cache.entry(info, gl::cache_buffers::local).bind(index);
|
||||
|
||||
glTexParameteri((GLenum)target, GL_TEXTURE_MAX_LEVEL, tex.mipmap() - 1);
|
||||
glTexParameteri((GLenum)target, GL_GENERATE_MIPMAP, tex.mipmap() > 1);
|
||||
|
||||
if ((info.format.flags & gl::texture_flags::allow_remap) != gl::texture_flags::none)
|
||||
{
|
||||
u8 remap_a = tex.remap() & 0x3;
|
||||
u8 remap_r = (tex.remap() >> 2) & 0x3;
|
||||
u8 remap_g = (tex.remap() >> 4) & 0x3;
|
||||
u8 remap_b = (tex.remap() >> 6) & 0x3;
|
||||
|
||||
glTexParameteri((GLenum)target, GL_TEXTURE_SWIZZLE_A, remap[remap_a]);
|
||||
glTexParameteri((GLenum)target, GL_TEXTURE_SWIZZLE_R, remap[remap_r]);
|
||||
glTexParameteri((GLenum)target, GL_TEXTURE_SWIZZLE_G, remap[remap_g]);
|
||||
glTexParameteri((GLenum)target, GL_TEXTURE_SWIZZLE_B, remap[remap_b]);
|
||||
}
|
||||
else
|
||||
{
|
||||
glTexParameteri((GLenum)target, GL_TEXTURE_SWIZZLE_A, remap[0]);
|
||||
glTexParameteri((GLenum)target, GL_TEXTURE_SWIZZLE_R, remap[1]);
|
||||
glTexParameteri((GLenum)target, GL_TEXTURE_SWIZZLE_G, remap[2]);
|
||||
glTexParameteri((GLenum)target, GL_TEXTURE_SWIZZLE_B, remap[3]);
|
||||
}
|
||||
|
||||
glTexParameteri((GLenum)target, GL_TEXTURE_WRAP_S, wrap(tex.wrap_s()));
|
||||
glTexParameteri((GLenum)target, GL_TEXTURE_WRAP_T, wrap(tex.wrap_t()));
|
||||
glTexParameteri((GLenum)target, GL_TEXTURE_WRAP_R, wrap(tex.wrap_r()));
|
||||
|
||||
glTexParameteri((GLenum)target, GL_TEXTURE_COMPARE_FUNC, gl_tex_zfunc[tex.zfunc()]);
|
||||
|
||||
glTexEnvi(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, (GLint)tex.bias());
|
||||
glTexParameteri((GLenum)target, GL_TEXTURE_MIN_LOD, (tex.min_lod() >> 8));
|
||||
glTexParameteri((GLenum)target, GL_TEXTURE_MAX_LOD, (tex.max_lod() >> 8));
|
||||
|
||||
glTexParameteri((GLenum)target, GL_TEXTURE_MIN_FILTER, gl_tex_min_filter[tex.min_filter()]);
|
||||
glTexParameteri((GLenum)target, GL_TEXTURE_MAG_FILTER, gl_tex_mag_filter[tex.mag_filter()]);
|
||||
glTexParameterf((GLenum)target, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_aniso(tex.max_aniso()));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,65 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include "OpenGL.h"
|
||||
#include <Utilities/types.h>
|
||||
|
||||
namespace gl
|
||||
{
|
||||
class texture_cache;
|
||||
}
|
||||
|
||||
namespace rsx
|
||||
{
|
||||
class texture;
|
||||
|
||||
namespace gl
|
||||
namespace gl_texture
|
||||
{
|
||||
class texture
|
||||
{
|
||||
u32 m_id = 0;
|
||||
u32 m_target = GL_TEXTURE_2D;
|
||||
|
||||
public:
|
||||
void create();
|
||||
|
||||
int gl_wrap(int wrap);
|
||||
|
||||
float max_aniso(int aniso);
|
||||
|
||||
inline static u8 convert_4_to_8(u8 v)
|
||||
{
|
||||
// Swizzle bits: 00001234 -> 12341234
|
||||
return (v << 4) | (v);
|
||||
}
|
||||
|
||||
inline static u8 convert_5_to_8(u8 v)
|
||||
{
|
||||
// Swizzle bits: 00012345 -> 12345123
|
||||
return (v << 3) | (v >> 2);
|
||||
}
|
||||
|
||||
inline static u8 convert_6_to_8(u8 v)
|
||||
{
|
||||
// Swizzle bits: 00123456 -> 12345612
|
||||
return (v << 2) | (v >> 4);
|
||||
}
|
||||
|
||||
void init(int index, rsx::texture& tex);
|
||||
|
||||
/**
|
||||
* If a format is marked as mandating expansion, any request to have the data uploaded to the GPU shall require that the pixel data
|
||||
* be decoded/expanded fully, regardless of whether the input is swizzled. This is because some formats behave differently when swizzled pixel data
|
||||
* is decoded and when data is fed directly, usually byte order is not the same. Forcing decoding/expanding fixes this but slows performance.
|
||||
*/
|
||||
static bool mandates_expansion(u32 format);
|
||||
|
||||
/**
|
||||
* The pitch modifier changes the pitch value supplied by the rsx::texture by supplying a suitable divisor or 0 if no change is needed.
|
||||
* The modified value, if any, is then used to supply to GL the UNPACK_ROW_LENGTH for the texture data to be supplied.
|
||||
*/
|
||||
static u16 get_pitch_modifier(u32 format);
|
||||
|
||||
void bind();
|
||||
void unbind();
|
||||
void remove();
|
||||
|
||||
void set_target(u32 target) { m_target = target; }
|
||||
void set_id(u32 id) { m_id = id; }
|
||||
u32 id() const;
|
||||
};
|
||||
void bind(gl::texture_cache& cache, int index, rsx::texture& tex);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -373,6 +373,11 @@ namespace rsx
|
|||
continue;
|
||||
}
|
||||
|
||||
if (m_internal_task_waiters.load(std::memory_order_relaxed))
|
||||
{
|
||||
do_internal_task();
|
||||
}
|
||||
|
||||
const u32 cmd = ReadIO32(get);
|
||||
const u32 count = (cmd >> 18) & 0x7ff;
|
||||
|
||||
|
@ -532,39 +537,32 @@ namespace rsx
|
|||
{
|
||||
std::lock_guard<std::mutex> lock{ m_mtx_task };
|
||||
|
||||
internal_task_entry &front = m_internal_tasks.front();
|
||||
auto &front = m_internal_tasks.front();
|
||||
|
||||
if (front.callback())
|
||||
{
|
||||
front.promise.set_value();
|
||||
m_internal_tasks.pop_front();
|
||||
}
|
||||
front();
|
||||
m_internal_tasks.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
std::future<void> thread::add_internal_task(std::function<bool()> callback)
|
||||
std::shared_future<void> thread::add_internal_task(std::function<void()> callback)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock{ m_mtx_task };
|
||||
m_internal_tasks.emplace_back(callback);
|
||||
|
||||
return m_internal_tasks.back().promise.get_future();
|
||||
return m_internal_tasks.back().get_future();
|
||||
}
|
||||
|
||||
void thread::invoke(std::function<bool()> callback)
|
||||
void thread::invoke(std::function<void()> callback)
|
||||
{
|
||||
if (get_thread_ctrl() == thread_ctrl::get_current())
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (callback())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
callback();
|
||||
}
|
||||
else
|
||||
{
|
||||
++m_internal_task_waiters;
|
||||
add_internal_task(callback).wait();
|
||||
--m_internal_task_waiters;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -333,23 +333,14 @@ namespace rsx
|
|||
|
||||
private:
|
||||
std::mutex m_mtx_task;
|
||||
std::atomic<uint> m_internal_task_waiters{ 0 };
|
||||
|
||||
struct internal_task_entry
|
||||
{
|
||||
std::function<bool()> callback;
|
||||
std::promise<void> promise;
|
||||
|
||||
internal_task_entry(std::function<bool()> callback) : callback(callback)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::deque<internal_task_entry> m_internal_tasks;
|
||||
std::deque<std::packaged_task<void()>> m_internal_tasks;
|
||||
void do_internal_task();
|
||||
|
||||
public:
|
||||
std::future<void> add_internal_task(std::function<bool()> callback);
|
||||
void invoke(std::function<bool()> callback);
|
||||
std::shared_future<void> add_internal_task(std::function<void()> callback);
|
||||
void invoke(std::function<void()> callback);
|
||||
|
||||
/**
|
||||
* Fill buffer with 4x4 scale offset matrix.
|
||||
|
|
Loading…
Add table
Reference in a new issue