2015-05-19 06:21:33 +02:00
|
|
|
// Copyright 2015 Citra Emulator Project
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
2015-08-25 04:11:46 +02:00
|
|
|
#include "common/hash.h"
|
2015-05-19 06:21:33 +02:00
|
|
|
#include "common/make_unique.h"
|
|
|
|
#include "common/math_util.h"
|
2015-08-17 23:25:21 +02:00
|
|
|
#include "common/microprofile.h"
|
2015-05-24 21:16:22 +02:00
|
|
|
#include "common/vector_math.h"
|
2015-05-19 06:21:33 +02:00
|
|
|
|
|
|
|
#include "core/memory.h"
|
|
|
|
|
|
|
|
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
|
|
|
|
#include "video_core/renderer_opengl/pica_to_gl.h"
|
|
|
|
#include "video_core/debug_utils/debug_utils.h"
|
|
|
|
|
|
|
|
RasterizerCacheOpenGL::~RasterizerCacheOpenGL() {
|
|
|
|
FullFlush();
|
|
|
|
}
|
|
|
|
|
2015-08-17 23:25:21 +02:00
|
|
|
MICROPROFILE_DEFINE(OpenGL_TextureUpload, "OpenGL", "Texture Upload", MP_RGB(128, 64, 192));
|
|
|
|
|
2015-05-19 06:21:33 +02:00
|
|
|
void RasterizerCacheOpenGL::LoadAndBindTexture(OpenGLState &state, unsigned texture_unit, const Pica::Regs::FullTextureConfig& config) {
|
|
|
|
PAddr texture_addr = config.config.GetPhysicalAddress();
|
|
|
|
const auto cached_texture = texture_cache.find(texture_addr);
|
|
|
|
|
|
|
|
if (cached_texture != texture_cache.end()) {
|
|
|
|
state.texture_units[texture_unit].texture_2d = cached_texture->second->texture.handle;
|
|
|
|
state.Apply();
|
|
|
|
} else {
|
2015-08-17 23:25:21 +02:00
|
|
|
MICROPROFILE_SCOPE(OpenGL_TextureUpload);
|
|
|
|
|
2015-05-19 06:21:33 +02:00
|
|
|
std::unique_ptr<CachedTexture> new_texture = Common::make_unique<CachedTexture>();
|
|
|
|
|
|
|
|
new_texture->texture.Create();
|
|
|
|
state.texture_units[texture_unit].texture_2d = new_texture->texture.handle;
|
|
|
|
state.Apply();
|
2015-08-06 04:22:06 +02:00
|
|
|
glActiveTexture(GL_TEXTURE0 + texture_unit);
|
2015-05-19 06:21:33 +02:00
|
|
|
|
2015-07-21 23:26:09 +02:00
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, PicaToGL::TextureFilterMode(config.config.mag_filter));
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, PicaToGL::TextureFilterMode(config.config.min_filter));
|
2015-05-19 06:21:33 +02:00
|
|
|
|
2015-07-23 00:53:36 +02:00
|
|
|
GLenum wrap_s = PicaToGL::WrapMode(config.config.wrap_s);
|
|
|
|
GLenum wrap_t = PicaToGL::WrapMode(config.config.wrap_t);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_s);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_t);
|
|
|
|
|
|
|
|
if (wrap_s == GL_CLAMP_TO_BORDER || wrap_t == GL_CLAMP_TO_BORDER) {
|
|
|
|
auto border_color = PicaToGL::ColorRGBA8((u8*)&config.config.border_color.r);
|
|
|
|
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color.data());
|
|
|
|
}
|
2015-05-19 06:21:33 +02:00
|
|
|
|
|
|
|
const auto info = Pica::DebugUtils::TextureInfo::FromPicaRegister(config.config, config.format);
|
2015-08-25 04:11:46 +02:00
|
|
|
u8* texture_src_data = Memory::GetPhysicalPointer(texture_addr);
|
2015-05-19 06:21:33 +02:00
|
|
|
|
|
|
|
new_texture->width = info.width;
|
|
|
|
new_texture->height = info.height;
|
2015-08-25 04:11:46 +02:00
|
|
|
new_texture->size = info.stride * info.height;
|
|
|
|
new_texture->addr = texture_addr;
|
|
|
|
new_texture->hash = Common::ComputeHash64(texture_src_data, new_texture->size);
|
2015-05-19 06:21:33 +02:00
|
|
|
|
|
|
|
std::unique_ptr<Math::Vec4<u8>[]> temp_texture_buffer_rgba(new Math::Vec4<u8>[info.width * info.height]);
|
|
|
|
|
|
|
|
for (int y = 0; y < info.height; ++y) {
|
|
|
|
for (int x = 0; x < info.width; ++x) {
|
|
|
|
temp_texture_buffer_rgba[x + info.width * y] = Pica::DebugUtils::LookupTexture(texture_src_data, x, info.height - 1 - y, info);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, info.width, info.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, temp_texture_buffer_rgba.get());
|
|
|
|
|
|
|
|
texture_cache.emplace(texture_addr, std::move(new_texture));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-25 04:11:46 +02:00
|
|
|
void RasterizerCacheOpenGL::NotifyFlush(PAddr addr, u32 size, bool ignore_hash) {
|
2015-05-19 06:21:33 +02:00
|
|
|
// Flush any texture that falls in the flushed region
|
|
|
|
// TODO: Optimize by also inserting upper bound (addr + size) of each texture into the same map and also narrow using lower_bound
|
|
|
|
auto cache_upper_bound = texture_cache.upper_bound(addr + size);
|
2015-08-25 04:11:46 +02:00
|
|
|
|
2015-05-19 06:21:33 +02:00
|
|
|
for (auto it = texture_cache.begin(); it != cache_upper_bound;) {
|
2015-08-25 04:11:46 +02:00
|
|
|
const auto& info = *it->second;
|
|
|
|
|
|
|
|
// Flush the texture only if the memory region intersects and a change is detected
|
|
|
|
if (MathUtil::IntervalsIntersect(addr, size, info.addr, info.size) &&
|
|
|
|
(ignore_hash || info.hash != Common::ComputeHash64(Memory::GetPhysicalPointer(info.addr), info.size))) {
|
|
|
|
|
2015-05-19 06:21:33 +02:00
|
|
|
it = texture_cache.erase(it);
|
|
|
|
} else {
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void RasterizerCacheOpenGL::FullFlush() {
|
|
|
|
texture_cache.clear();
|
|
|
|
}
|