diff --git a/Utilities/types.h b/Utilities/types.h index ad631e1ea8..1e7de850e5 100644 --- a/Utilities/types.h +++ b/Utilities/types.h @@ -108,7 +108,7 @@ struct fnv_1a_hasher } }; -struct binary_equals +struct bitwise_equals { template bool operator()(const TypeA& a, const TypeB& b) const diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.cpp b/rpcs3/Emu/RSX/GL/GLGSRender.cpp index 06a380583a..925c5192a2 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.cpp +++ b/rpcs3/Emu/RSX/GL/GLGSRender.cpp @@ -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; } diff --git a/rpcs3/Emu/RSX/GL/GLGSRender.h b/rpcs3/Emu/RSX/GL/GLGSRender.h index d994de66e9..471a2d0ddf 100644 --- a/rpcs3/Emu/RSX/GL/GLGSRender.h +++ b/rpcs3/Emu/RSX/GL/GLGSRender.h @@ -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; diff --git a/rpcs3/Emu/RSX/GL/gl_helpers.h b/rpcs3/Emu/RSX/GL/gl_helpers.h index 73b78b77a7..da436fe139 100644 --- a/rpcs3/Emu/RSX/GL/gl_helpers.h +++ b/rpcs3/Emu/RSX/GL/gl_helpers.h @@ -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) diff --git a/rpcs3/Emu/RSX/GL/gl_texture_cache.cpp b/rpcs3/Emu/RSX/GL/gl_texture_cache.cpp index cffa2fd547..6876981406 100644 --- a/rpcs3/Emu/RSX/GL/gl_texture_cache.cpp +++ b/rpcs3/Emu/RSX/GL/gl_texture_cache.cpp @@ -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 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 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) - { - 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 callback) + { + for (auto &entry : m_textures) + { + callback(entry.second); + } + } + + void protected_region::for_each(u32 start_address, u32 size, std::function 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(); } } diff --git a/rpcs3/Emu/RSX/GL/gl_texture_cache.h b/rpcs3/Emu/RSX/GL/gl_texture_cache.h index 87e9847ad9..0810600f86 100644 --- a/rpcs3/Emu/RSX/GL/gl_texture_cache.h +++ b/rpcs3/Emu/RSX/GL/gl_texture_cache.h @@ -2,7 +2,6 @@ #include #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 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 callback); + void for_each(u32 start_address, u32 size, std::function 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 m_texture_cache; - std::vector m_rtt_cache; - u32 m_frame_ctr = 0; + std::map 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 find_and_invalidate_in_range(u32 base, u32 limit); - void lock_invalidated_ranges(const std::vector &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(); }; } diff --git a/rpcs3/Emu/RSX/GL/rsx_gl_texture.cpp b/rpcs3/Emu/RSX/GL/rsx_gl_texture.cpp index 61d1dca3d9..2bd43fd868 100644 --- a/rpcs3/Emu/RSX/GL/rsx_gl_texture.cpp +++ b/rpcs3/Emu/RSX/GL/rsx_gl_texture.cpp @@ -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 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(tex.width(), 1); + info.height = std::max(tex.height(), 1); + info.depth = std::max(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 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(texture_data), gsl::narrow(texture_data_sz) }, tex, 256); - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - } - else - { - texture_data = vm::ps3::_ptr(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())); } + diff --git a/rpcs3/Emu/RSX/GL/rsx_gl_texture.h b/rpcs3/Emu/RSX/GL/rsx_gl_texture.h index cd20a7595f..172b119a03 100644 --- a/rpcs3/Emu/RSX/GL/rsx_gl_texture.h +++ b/rpcs3/Emu/RSX/GL/rsx_gl_texture.h @@ -1,65 +1,20 @@ #pragma once #include "OpenGL.h" +#include + +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); } } + diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index 2c48036c73..14b2843403 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -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 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 thread::add_internal_task(std::function callback) + std::shared_future thread::add_internal_task(std::function callback) { std::lock_guard 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 callback) + void thread::invoke(std::function 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; } } diff --git a/rpcs3/Emu/RSX/RSXThread.h b/rpcs3/Emu/RSX/RSXThread.h index 14fb2aeba8..358a20bc15 100644 --- a/rpcs3/Emu/RSX/RSXThread.h +++ b/rpcs3/Emu/RSX/RSXThread.h @@ -333,23 +333,14 @@ namespace rsx private: std::mutex m_mtx_task; + std::atomic m_internal_task_waiters{ 0 }; - struct internal_task_entry - { - std::function callback; - std::promise promise; - - internal_task_entry(std::function callback) : callback(callback) - { - } - }; - - std::deque m_internal_tasks; + std::deque> m_internal_tasks; void do_internal_task(); public: - std::future add_internal_task(std::function callback); - void invoke(std::function callback); + std::shared_future add_internal_task(std::function callback); + void invoke(std::function callback); /** * Fill buffer with 4x4 scale offset matrix.