A texture cache that never invalidates.
This commit is contained in:
parent
79179c28ef
commit
718762746a
|
@ -198,12 +198,14 @@ LRESULT Win32Control::WndProc(HWND hWnd, UINT message, WPARAM wParam,
|
|||
LPARAM lParam) {
|
||||
if (message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) {
|
||||
if (HandleMouse(message, wParam, lParam)) {
|
||||
SetFocus(hwnd_);
|
||||
return 0;
|
||||
} else {
|
||||
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||
}
|
||||
} else if (message >= WM_KEYFIRST && message <= WM_KEYLAST) {
|
||||
if (HandleKeyboard(message, wParam, lParam)) {
|
||||
SetFocus(hwnd_);
|
||||
return 0;
|
||||
} else {
|
||||
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||
|
|
|
@ -169,7 +169,7 @@ bool CommandProcessor::SetupGL() {
|
|||
}
|
||||
|
||||
// Texture cache that keeps track of any textures/samplers used.
|
||||
if (!texture_cache_.Initialize(&scratch_buffer_)) {
|
||||
if (!texture_cache_.Initialize(membase_, &scratch_buffer_)) {
|
||||
PLOGE("Unable to initialize texture cache");
|
||||
return false;
|
||||
}
|
||||
|
@ -843,6 +843,9 @@ bool CommandProcessor::ExecutePacketType3_XE_SWAP(RingbufferReader* reader,
|
|||
PrepareForWait();
|
||||
swap_handler_(swap_params);
|
||||
ReturnFromWait();
|
||||
|
||||
// Remove any dead textures, etc.
|
||||
texture_cache_.Scavenge();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1340,9 +1343,6 @@ bool CommandProcessor::IssueDraw(DrawCommand* draw_command) {
|
|||
return IssueCopy(draw_command);
|
||||
}
|
||||
|
||||
// TODO(benvanik): actually cache things >_>
|
||||
texture_cache_.Clear();
|
||||
|
||||
// Allocate a state data block.
|
||||
// Everything the shaders access lives here.
|
||||
auto allocation = scratch_buffer_.Acquire(sizeof(UniformDataBlock));
|
||||
|
@ -2199,10 +2199,7 @@ bool CommandProcessor::PopulateSampler(DrawCommand* draw_command,
|
|||
return true; // invalid texture used
|
||||
}
|
||||
|
||||
uint32_t guest_base = fetch.address << 12;
|
||||
void* host_base = membase_ + guest_base;
|
||||
auto entry_view = texture_cache_.Demand(host_base, texture_info.input_length,
|
||||
texture_info, sampler_info);
|
||||
auto entry_view = texture_cache_.Demand(texture_info, sampler_info);
|
||||
if (!entry_view) {
|
||||
// Unable to create/fetch/etc.
|
||||
XELOGE("Failed to demand texture");
|
||||
|
|
|
@ -22,138 +22,139 @@ using namespace xe::gpu::xenos;
|
|||
extern "C" GLEWContext* glewGetContext();
|
||||
extern "C" WGLEWContext* wglewGetContext();
|
||||
|
||||
TextureCache::TextureCache() {
|
||||
//
|
||||
TextureCache::TextureCache() : membase_(nullptr), scratch_buffer_(nullptr) {
|
||||
invalidated_textures_sets_[0].reserve(64);
|
||||
invalidated_textures_sets_[1].reserve(64);
|
||||
invalidated_textures_ = &invalidated_textures_sets_[0];
|
||||
}
|
||||
|
||||
TextureCache::~TextureCache() { Shutdown(); }
|
||||
|
||||
bool TextureCache::Initialize(CircularBuffer* scratch_buffer) {
|
||||
bool TextureCache::Initialize(uint8_t* membase,
|
||||
CircularBuffer* scratch_buffer) {
|
||||
membase_ = membase;
|
||||
scratch_buffer_ = scratch_buffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
void TextureCache::Shutdown() {
|
||||
Clear();
|
||||
//
|
||||
void TextureCache::Shutdown() { Clear(); }
|
||||
|
||||
void TextureCache::Scavenge() {
|
||||
invalidated_textures_mutex_.lock();
|
||||
std::vector<TextureEntry*>& invalidated_textures = *invalidated_textures_;
|
||||
if (invalidated_textures_ == &invalidated_textures_sets_[0]) {
|
||||
invalidated_textures_ = &invalidated_textures_sets_[1];
|
||||
} else {
|
||||
invalidated_textures_ = &invalidated_textures_sets_[0];
|
||||
}
|
||||
invalidated_textures_mutex_.unlock();
|
||||
if (invalidated_textures.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& entry : invalidated_textures) {
|
||||
EvictTexture(entry);
|
||||
}
|
||||
invalidated_textures.clear();
|
||||
}
|
||||
|
||||
void TextureCache::Clear() {
|
||||
for (auto& entry : entries_) {
|
||||
for (auto& view : entry.views) {
|
||||
glMakeTextureHandleNonResidentARB(view.texture_sampler_handle);
|
||||
glDeleteSamplers(1, &view.sampler);
|
||||
}
|
||||
glDeleteTextures(1, &entry.base_texture);
|
||||
// Kill all textures - some may be in the eviction list, but that's fine
|
||||
// as we will clear that below.
|
||||
while (texture_entries_.size()) {
|
||||
auto entry = texture_entries_.begin()->second;
|
||||
EvictTexture(entry);
|
||||
}
|
||||
|
||||
// Samplers must go last, as textures depend on them.
|
||||
while (sampler_entries_.size()) {
|
||||
auto entry = sampler_entries_.begin()->second;
|
||||
EvictSampler(entry);
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(invalidated_textures_mutex_);
|
||||
invalidated_textures_sets_[0].clear();
|
||||
invalidated_textures_sets_[1].clear();
|
||||
}
|
||||
entries_.clear();
|
||||
}
|
||||
|
||||
TextureCache::EntryView* TextureCache::Demand(void* host_base, size_t length,
|
||||
const TextureInfo& texture_info,
|
||||
const SamplerInfo& sampler_info) {
|
||||
entries_.emplace_back(Entry());
|
||||
auto& entry = entries_.back();
|
||||
entry.texture_info = texture_info;
|
||||
//typedef void (*WriteWatchCallback)(void* context, void* data, void* address);
|
||||
//uintptr_t AddWriteWatch(void* address, size_t length,
|
||||
// WriteWatchCallback callback, void* callback_context,
|
||||
// void* callback_data) {
|
||||
// //
|
||||
//}
|
||||
|
||||
GLenum target;
|
||||
switch (texture_info.dimension) {
|
||||
case Dimension::k1D:
|
||||
target = GL_TEXTURE_1D;
|
||||
break;
|
||||
case Dimension::k2D:
|
||||
target = GL_TEXTURE_2D;
|
||||
break;
|
||||
case Dimension::k3D:
|
||||
target = GL_TEXTURE_3D;
|
||||
break;
|
||||
case Dimension::kCube:
|
||||
target = GL_TEXTURE_CUBE_MAP;
|
||||
break;
|
||||
TextureCache::TextureEntryView* TextureCache::Demand(
|
||||
const TextureInfo& texture_info, const SamplerInfo& sampler_info) {
|
||||
uint64_t texture_hash = texture_info.hash();
|
||||
auto texture_entry = LookupOrInsertTexture(texture_info, texture_hash);
|
||||
if (!texture_entry) {
|
||||
PLOGE("Failed to setup texture");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Setup the base texture.
|
||||
glCreateTextures(target, 1, &entry.base_texture);
|
||||
if (!SetupTexture(entry.base_texture, texture_info)) {
|
||||
PLOGE("Unable to setup texture parameters");
|
||||
return false;
|
||||
// We likely have the sampler in the texture view listing, so scan for it.
|
||||
uint64_t sampler_hash = sampler_info.hash();
|
||||
for (auto& it : texture_entry->views) {
|
||||
if (it->sampler_hash == sampler_hash) {
|
||||
// Found.
|
||||
return it.get();
|
||||
}
|
||||
}
|
||||
|
||||
// Upload/convert.
|
||||
bool uploaded = false;
|
||||
switch (texture_info.dimension) {
|
||||
case Dimension::k2D:
|
||||
uploaded = UploadTexture2D(entry.base_texture, host_base, length,
|
||||
texture_info, sampler_info);
|
||||
break;
|
||||
case Dimension::k1D:
|
||||
case Dimension::k3D:
|
||||
case Dimension::kCube:
|
||||
assert_unhandled_case(texture_info.dimension);
|
||||
return false;
|
||||
}
|
||||
if (!uploaded) {
|
||||
PLOGE("Failed to convert/upload texture");
|
||||
return false;
|
||||
// No existing view found - build it.
|
||||
auto sampler_entry = LookupOrInsertSampler(sampler_info, sampler_hash);
|
||||
if (!sampler_entry) {
|
||||
PLOGE("Failed to setup texture sampler");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
entry.views.emplace_back(EntryView());
|
||||
auto& entry_view = entry.views.back();
|
||||
entry_view.sampler_info = sampler_info;
|
||||
|
||||
// Setup the sampler.
|
||||
glCreateSamplers(1, &entry_view.sampler);
|
||||
if (!SetupSampler(entry_view.sampler, texture_info, sampler_info)) {
|
||||
PLOGE("Unable to setup texture sampler parameters");
|
||||
return false;
|
||||
}
|
||||
auto view = std::make_unique<TextureEntryView>();
|
||||
view->sampler = sampler_entry;
|
||||
view->sampler_hash = sampler_hash;
|
||||
view->texture_sampler_handle = 0;
|
||||
|
||||
// Get the uvec2 handle to the texture/sampler pair and make it resident.
|
||||
// The handle can be passed directly to the shader.
|
||||
entry_view.texture_sampler_handle =
|
||||
glGetTextureSamplerHandleARB(entry.base_texture, entry_view.sampler);
|
||||
if (!entry_view.texture_sampler_handle) {
|
||||
view->texture_sampler_handle = glGetTextureSamplerHandleARB(
|
||||
texture_entry->handle, sampler_entry->handle);
|
||||
if (!view->texture_sampler_handle) {
|
||||
return nullptr;
|
||||
}
|
||||
glMakeTextureHandleResidentARB(entry_view.texture_sampler_handle);
|
||||
glMakeTextureHandleResidentARB(view->texture_sampler_handle);
|
||||
|
||||
return &entry_view;
|
||||
// Entry takes ownership.
|
||||
auto view_ptr = view.get();
|
||||
texture_entry->views.push_back(std::move(view));
|
||||
return view_ptr;
|
||||
}
|
||||
|
||||
bool TextureCache::SetupTexture(GLuint texture,
|
||||
const TextureInfo& texture_info) {
|
||||
// TODO(benvanik): texture mip levels.
|
||||
glTextureParameteri(texture, GL_TEXTURE_BASE_LEVEL, 0);
|
||||
glTextureParameteri(texture, GL_TEXTURE_MAX_LEVEL, 1);
|
||||
TextureCache::SamplerEntry* TextureCache::LookupOrInsertSampler(
|
||||
const SamplerInfo& sampler_info, uint64_t opt_hash) {
|
||||
const uint64_t hash = opt_hash ? opt_hash : sampler_info.hash();
|
||||
for (auto it = sampler_entries_.find(hash); it != sampler_entries_.end();
|
||||
++it) {
|
||||
if (it->second->sampler_info == sampler_info) {
|
||||
// Found in cache!
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-shader swizzle.
|
||||
// TODO(benvanik): can this be dynamic? Maybe per view?
|
||||
// We may have to emulate this in the shader.
|
||||
uint32_t swizzle_r = texture_info.swizzle & 0x7;
|
||||
uint32_t swizzle_g = (texture_info.swizzle >> 3) & 0x7;
|
||||
uint32_t swizzle_b = (texture_info.swizzle >> 6) & 0x7;
|
||||
uint32_t swizzle_a = (texture_info.swizzle >> 9) & 0x7;
|
||||
static const GLenum swizzle_map[] = {
|
||||
GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA, GL_ZERO, GL_ONE,
|
||||
};
|
||||
glTextureParameteri(texture, GL_TEXTURE_SWIZZLE_R, swizzle_map[swizzle_r]);
|
||||
glTextureParameteri(texture, GL_TEXTURE_SWIZZLE_G, swizzle_map[swizzle_g]);
|
||||
glTextureParameteri(texture, GL_TEXTURE_SWIZZLE_B, swizzle_map[swizzle_b]);
|
||||
glTextureParameteri(texture, GL_TEXTURE_SWIZZLE_A, swizzle_map[swizzle_a]);
|
||||
// Not found, create.
|
||||
auto entry = std::make_unique<SamplerEntry>();
|
||||
entry->sampler_info = sampler_info;
|
||||
glCreateSamplers(1, &entry->handle);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextureCache::SetupSampler(GLuint sampler, const TextureInfo& texture_info,
|
||||
const SamplerInfo& sampler_info) {
|
||||
// TODO(benvanik): border color from texture fetch.
|
||||
GLfloat border_color[4] = {0.0f};
|
||||
glSamplerParameterfv(sampler, GL_TEXTURE_BORDER_COLOR, border_color);
|
||||
glSamplerParameterfv(entry->handle, GL_TEXTURE_BORDER_COLOR, border_color);
|
||||
|
||||
// TODO(benvanik): setup LODs for mipmapping.
|
||||
glSamplerParameterf(sampler, GL_TEXTURE_LOD_BIAS, 0.0f);
|
||||
glSamplerParameterf(sampler, GL_TEXTURE_MIN_LOD, 0.0f);
|
||||
glSamplerParameterf(sampler, GL_TEXTURE_MAX_LOD, 0.0f);
|
||||
glSamplerParameterf(entry->handle, GL_TEXTURE_LOD_BIAS, 0.0f);
|
||||
glSamplerParameterf(entry->handle, GL_TEXTURE_MIN_LOD, 0.0f);
|
||||
glSamplerParameterf(entry->handle, GL_TEXTURE_MAX_LOD, 0.0f);
|
||||
|
||||
// Texture wrapping modes.
|
||||
// TODO(benvanik): not sure if the middle ones are correct.
|
||||
|
@ -167,11 +168,11 @@ bool TextureCache::SetupSampler(GLuint sampler, const TextureInfo& texture_info,
|
|||
GL_CLAMP_TO_BORDER, //
|
||||
GL_MIRROR_CLAMP_TO_BORDER_EXT, //
|
||||
};
|
||||
glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S,
|
||||
glSamplerParameteri(entry->handle, GL_TEXTURE_WRAP_S,
|
||||
wrap_map[sampler_info.clamp_u]);
|
||||
glSamplerParameteri(sampler, GL_TEXTURE_WRAP_T,
|
||||
glSamplerParameteri(entry->handle, GL_TEXTURE_WRAP_T,
|
||||
wrap_map[sampler_info.clamp_v]);
|
||||
glSamplerParameteri(sampler, GL_TEXTURE_WRAP_R,
|
||||
glSamplerParameteri(entry->handle, GL_TEXTURE_WRAP_R,
|
||||
wrap_map[sampler_info.clamp_w]);
|
||||
|
||||
// Texture level filtering.
|
||||
|
@ -192,7 +193,7 @@ bool TextureCache::SetupSampler(GLuint sampler, const TextureInfo& texture_info,
|
|||
break;
|
||||
default:
|
||||
assert_unhandled_case(sampler_info.mip_filter);
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
break;
|
||||
case ucode::TEX_FILTER_LINEAR:
|
||||
|
@ -210,12 +211,12 @@ bool TextureCache::SetupSampler(GLuint sampler, const TextureInfo& texture_info,
|
|||
break;
|
||||
default:
|
||||
assert_unhandled_case(sampler_info.mip_filter);
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert_unhandled_case(sampler_info.min_filter);
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
GLenum mag_filter;
|
||||
switch (sampler_info.mag_filter) {
|
||||
|
@ -227,15 +228,140 @@ bool TextureCache::SetupSampler(GLuint sampler, const TextureInfo& texture_info,
|
|||
break;
|
||||
default:
|
||||
assert_unhandled_case(mag_filter);
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, min_filter);
|
||||
glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, mag_filter);
|
||||
glSamplerParameteri(entry->handle, GL_TEXTURE_MIN_FILTER, min_filter);
|
||||
glSamplerParameteri(entry->handle, GL_TEXTURE_MAG_FILTER, mag_filter);
|
||||
|
||||
// TODO(benvanik): anisotropic filtering.
|
||||
// GL_TEXTURE_MAX_ANISOTROPY_EXT
|
||||
|
||||
return true;
|
||||
// Add to map - map takes ownership.
|
||||
auto entry_ptr = entry.get();
|
||||
sampler_entries_.insert({hash, entry.release()});
|
||||
return entry_ptr;
|
||||
}
|
||||
|
||||
void TextureCache::EvictSampler(SamplerEntry* entry) {
|
||||
glDeleteSamplers(1, &entry->handle);
|
||||
|
||||
for (auto it = sampler_entries_.find(entry->sampler_info.hash());
|
||||
it != sampler_entries_.end(); ++it) {
|
||||
if (it->second == entry) {
|
||||
sampler_entries_.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
delete entry;
|
||||
}
|
||||
|
||||
TextureCache::TextureEntry* TextureCache::LookupOrInsertTexture(
|
||||
const TextureInfo& texture_info, uint64_t opt_hash) {
|
||||
const uint64_t hash = opt_hash ? opt_hash : texture_info.hash();
|
||||
for (auto it = texture_entries_.find(hash); it != texture_entries_.end();
|
||||
++it) {
|
||||
if (it->second->texture_info == texture_info) {
|
||||
// Found in cache!
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
// Not found, create.
|
||||
auto entry = std::make_unique<TextureEntry>();
|
||||
entry->texture_info = texture_info;
|
||||
entry->handle = 0;
|
||||
|
||||
GLenum target;
|
||||
switch (texture_info.dimension) {
|
||||
case Dimension::k1D:
|
||||
target = GL_TEXTURE_1D;
|
||||
break;
|
||||
case Dimension::k2D:
|
||||
target = GL_TEXTURE_2D;
|
||||
break;
|
||||
case Dimension::k3D:
|
||||
target = GL_TEXTURE_3D;
|
||||
break;
|
||||
case Dimension::kCube:
|
||||
target = GL_TEXTURE_CUBE_MAP;
|
||||
break;
|
||||
}
|
||||
|
||||
// Setup the base texture.
|
||||
glCreateTextures(target, 1, &entry->handle);
|
||||
|
||||
// TODO(benvanik): texture mip levels.
|
||||
glTextureParameteri(entry->handle, GL_TEXTURE_BASE_LEVEL, 0);
|
||||
glTextureParameteri(entry->handle, GL_TEXTURE_MAX_LEVEL, 1);
|
||||
|
||||
// Pre-shader swizzle.
|
||||
// TODO(benvanik): can this be dynamic? Maybe per view?
|
||||
// We may have to emulate this in the shader.
|
||||
uint32_t swizzle_r = texture_info.swizzle & 0x7;
|
||||
uint32_t swizzle_g = (texture_info.swizzle >> 3) & 0x7;
|
||||
uint32_t swizzle_b = (texture_info.swizzle >> 6) & 0x7;
|
||||
uint32_t swizzle_a = (texture_info.swizzle >> 9) & 0x7;
|
||||
static const GLenum swizzle_map[] = {
|
||||
GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA, GL_ZERO, GL_ONE,
|
||||
};
|
||||
glTextureParameteri(entry->handle, GL_TEXTURE_SWIZZLE_R,
|
||||
swizzle_map[swizzle_r]);
|
||||
glTextureParameteri(entry->handle, GL_TEXTURE_SWIZZLE_G,
|
||||
swizzle_map[swizzle_g]);
|
||||
glTextureParameteri(entry->handle, GL_TEXTURE_SWIZZLE_B,
|
||||
swizzle_map[swizzle_b]);
|
||||
glTextureParameteri(entry->handle, GL_TEXTURE_SWIZZLE_A,
|
||||
swizzle_map[swizzle_a]);
|
||||
|
||||
// Upload/convert.
|
||||
bool uploaded = false;
|
||||
switch (texture_info.dimension) {
|
||||
case Dimension::k2D:
|
||||
uploaded = UploadTexture2D(entry->handle, texture_info);
|
||||
break;
|
||||
case Dimension::k1D:
|
||||
case Dimension::k3D:
|
||||
case Dimension::kCube:
|
||||
assert_unhandled_case(texture_info.dimension);
|
||||
return false;
|
||||
}
|
||||
if (!uploaded) {
|
||||
PLOGE("Failed to convert/upload texture");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// AddWriteWatch(host_base, length, [](void* context_ptr, void* data_ptr,
|
||||
// void* address) {
|
||||
// //
|
||||
//}, this, &entry);
|
||||
|
||||
// Add to map - map takes ownership.
|
||||
auto entry_ptr = entry.get();
|
||||
texture_entries_.insert({hash, entry.release()});
|
||||
return entry_ptr;
|
||||
}
|
||||
|
||||
void TextureCache::EvictTexture(TextureEntry* entry) {
|
||||
/*if (entry->write_watch_handle) {
|
||||
// remove from watch list
|
||||
}*/
|
||||
|
||||
for (auto& view : entry->views) {
|
||||
glMakeTextureHandleNonResidentARB(view->texture_sampler_handle);
|
||||
}
|
||||
glDeleteTextures(1, &entry->handle);
|
||||
|
||||
uint64_t texture_hash = entry->texture_info.hash();
|
||||
for (auto it = texture_entries_.find(texture_hash);
|
||||
it != texture_entries_.end(); ++it) {
|
||||
if (it->second == entry) {
|
||||
texture_entries_.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
delete entry;
|
||||
}
|
||||
|
||||
void TextureSwap(Endian endianness, void* dest, const void* src,
|
||||
|
@ -266,11 +392,10 @@ void TextureSwap(Endian endianness, void* dest, const void* src,
|
|||
}
|
||||
}
|
||||
|
||||
bool TextureCache::UploadTexture2D(GLuint texture, void* host_base,
|
||||
size_t length,
|
||||
const TextureInfo& texture_info,
|
||||
const SamplerInfo& sampler_info) {
|
||||
assert_true(length == texture_info.input_length);
|
||||
bool TextureCache::UploadTexture2D(GLuint texture,
|
||||
const TextureInfo& texture_info) {
|
||||
auto host_address =
|
||||
reinterpret_cast<const uint8_t*>(membase_ + texture_info.guest_address);
|
||||
|
||||
GLenum internal_format = GL_RGBA8;
|
||||
GLenum format = GL_RGBA;
|
||||
|
@ -433,13 +558,13 @@ bool TextureCache::UploadTexture2D(GLuint texture, void* host_base,
|
|||
if (texture_info.size_2d.input_pitch ==
|
||||
texture_info.size_2d.logical_pitch) {
|
||||
// Fast path copy entire image.
|
||||
TextureSwap(texture_info.endianness, allocation.host_ptr, host_base,
|
||||
TextureSwap(texture_info.endianness, allocation.host_ptr, host_address,
|
||||
unpack_length);
|
||||
} else {
|
||||
// Slow path copy row-by-row because strides differ.
|
||||
// UNPACK_ROW_LENGTH only works for uncompressed images, and likely does
|
||||
// this exact thing under the covers, so we just always do it here.
|
||||
const uint8_t* src = reinterpret_cast<const uint8_t*>(host_base);
|
||||
const uint8_t* src = host_address;
|
||||
uint8_t* dest = reinterpret_cast<uint8_t*>(allocation.host_ptr);
|
||||
for (uint32_t y = 0; y < texture_info.size_2d.block_height; y++) {
|
||||
TextureSwap(texture_info.endianness, dest, src,
|
||||
|
@ -452,7 +577,7 @@ bool TextureCache::UploadTexture2D(GLuint texture, void* host_base,
|
|||
// Untile image.
|
||||
// We could do this in a shader to speed things up, as this is pretty slow.
|
||||
// TODO(benvanik): optimize this inner loop (or work by tiles).
|
||||
uint8_t* src = reinterpret_cast<uint8_t*>(host_base);
|
||||
const uint8_t* src = host_address;
|
||||
uint8_t* dest = reinterpret_cast<uint8_t*>(allocation.host_ptr);
|
||||
uint32_t output_pitch =
|
||||
(texture_info.size_2d.output_width / texture_info.block_size) *
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
#ifndef XENIA_GPU_GL4_TEXTURE_CACHE_H_
|
||||
#define XENIA_GPU_GL4_TEXTURE_CACHE_H_
|
||||
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <xenia/gpu/gl4/circular_buffer.h>
|
||||
|
@ -23,39 +25,51 @@ namespace gl4 {
|
|||
|
||||
class TextureCache {
|
||||
public:
|
||||
struct EntryView {
|
||||
struct SamplerEntry {
|
||||
SamplerInfo sampler_info;
|
||||
GLuint sampler;
|
||||
GLuint handle;
|
||||
};
|
||||
struct TextureEntryView {
|
||||
SamplerEntry* sampler;
|
||||
uint64_t sampler_hash;
|
||||
GLuint64 texture_sampler_handle;
|
||||
};
|
||||
struct Entry {
|
||||
struct TextureEntry {
|
||||
TextureInfo texture_info;
|
||||
GLuint base_texture;
|
||||
std::vector<EntryView> views;
|
||||
GLuint handle;
|
||||
std::vector<std::unique_ptr<TextureEntryView>> views;
|
||||
};
|
||||
|
||||
TextureCache();
|
||||
~TextureCache();
|
||||
|
||||
bool Initialize(CircularBuffer* scratch_buffer);
|
||||
bool Initialize(uint8_t* membase, CircularBuffer* scratch_buffer);
|
||||
void Shutdown();
|
||||
|
||||
void Scavenge();
|
||||
void Clear();
|
||||
|
||||
EntryView* Demand(void* host_base, size_t length,
|
||||
const TextureInfo& texture_info,
|
||||
const SamplerInfo& sampler_info);
|
||||
TextureEntryView* Demand(const TextureInfo& texture_info,
|
||||
const SamplerInfo& sampler_info);
|
||||
|
||||
private:
|
||||
bool SetupTexture(GLuint texture, const TextureInfo& texture_info);
|
||||
bool SetupSampler(GLuint sampler, const TextureInfo& texture_info,
|
||||
const SamplerInfo& sampler_info);
|
||||
SamplerEntry* LookupOrInsertSampler(const SamplerInfo& sampler_info,
|
||||
uint64_t opt_hash = 0);
|
||||
void EvictSampler(SamplerEntry* entry);
|
||||
TextureEntry* LookupOrInsertTexture(const TextureInfo& texture_info,
|
||||
uint64_t opt_hash = 0);
|
||||
void EvictTexture(TextureEntry* entry);
|
||||
|
||||
bool UploadTexture2D(GLuint texture, void* host_base, size_t length,
|
||||
const TextureInfo& texture_info,
|
||||
const SamplerInfo& sampler_info);
|
||||
bool UploadTexture2D(GLuint texture, const TextureInfo& texture_info);
|
||||
|
||||
uint8_t* membase_;
|
||||
CircularBuffer* scratch_buffer_;
|
||||
std::vector<Entry> entries_;
|
||||
std::unordered_map<uint64_t, SamplerEntry*> sampler_entries_;
|
||||
std::unordered_map<uint64_t, TextureEntry*> texture_entries_;
|
||||
|
||||
std::mutex invalidated_textures_mutex_;
|
||||
std::vector<TextureEntry*>* invalidated_textures_;
|
||||
std::vector<TextureEntry*> invalidated_textures_sets_[2];
|
||||
};
|
||||
|
||||
} // namespace gl4
|
||||
|
|
|
@ -9,12 +9,16 @@
|
|||
|
||||
#include <xenia/gpu/sampler_info.h>
|
||||
|
||||
#include <third_party/xxhash/xxhash.h>
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
|
||||
bool SamplerInfo::Prepare(const xenos::xe_gpu_texture_fetch_t& fetch,
|
||||
const ucode::instr_fetch_tex_t& fetch_instr,
|
||||
SamplerInfo* out_info) {
|
||||
std::memset(out_info, 0, sizeof(SamplerInfo));
|
||||
|
||||
out_info->min_filter = static_cast<ucode::instr_tex_filter_t>(
|
||||
fetch_instr.min_filter == 3 ? fetch.min_filter : fetch_instr.min_filter);
|
||||
out_info->mag_filter = static_cast<ucode::instr_tex_filter_t>(
|
||||
|
@ -27,5 +31,9 @@ bool SamplerInfo::Prepare(const xenos::xe_gpu_texture_fetch_t& fetch,
|
|||
return true;
|
||||
}
|
||||
|
||||
uint64_t SamplerInfo::hash() const {
|
||||
return XXH64(this, sizeof(SamplerInfo), 0);
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace xe
|
||||
|
|
|
@ -28,6 +28,7 @@ struct SamplerInfo {
|
|||
const ucode::instr_fetch_tex_t& fetch_instr,
|
||||
SamplerInfo* out_info);
|
||||
|
||||
uint64_t hash() const;
|
||||
bool operator==(const SamplerInfo& other) const {
|
||||
return min_filter == other.min_filter && mag_filter == other.mag_filter &&
|
||||
mip_filter == other.mip_filter && clamp_u == other.clamp_u &&
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
#include <xenia/gpu/texture_info.h>
|
||||
|
||||
#include <third_party/xxhash/xxhash.h>
|
||||
|
||||
#include <poly/math.h>
|
||||
|
||||
namespace xe {
|
||||
|
@ -19,13 +21,16 @@ using namespace xe::gpu::xenos;
|
|||
|
||||
bool TextureInfo::Prepare(const xe_gpu_texture_fetch_t& fetch,
|
||||
TextureInfo* out_info) {
|
||||
std::memset(out_info, 0, sizeof(TextureInfo));
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/cc308051(v=vs.85).aspx
|
||||
// a2xx_sq_surfaceformat
|
||||
|
||||
auto& info = *out_info;
|
||||
info.guest_address = fetch.address << 12;
|
||||
info.swizzle = fetch.swizzle;
|
||||
|
||||
info.dimension = static_cast<Dimension>(fetch.dimension);
|
||||
info.width = info.height = info.depth = 0;
|
||||
switch (info.dimension) {
|
||||
case Dimension::k1D:
|
||||
info.width = fetch.size_1d.width;
|
||||
|
@ -240,5 +245,9 @@ uint32_t TextureInfo::TiledOffset2DInner(uint32_t x, uint32_t y, uint32_t bpp,
|
|||
((y & 16) << 7) + (((((y & 8) >> 2) + (x >> 3)) & 3) << 6);
|
||||
}
|
||||
|
||||
uint64_t TextureInfo::hash() const {
|
||||
return XXH64(this, sizeof(TextureInfo), 0);
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace xe
|
||||
|
|
|
@ -85,6 +85,8 @@ enum class TextureFormat : uint32_t {
|
|||
};
|
||||
|
||||
struct TextureInfo {
|
||||
uint32_t guest_address;
|
||||
uint32_t input_length;
|
||||
uint32_t swizzle;
|
||||
Dimension dimension;
|
||||
uint32_t width;
|
||||
|
@ -95,7 +97,6 @@ struct TextureInfo {
|
|||
xenos::Endian endianness;
|
||||
bool is_tiled;
|
||||
bool is_compressed;
|
||||
uint32_t input_length;
|
||||
|
||||
TextureFormat format;
|
||||
|
||||
|
@ -129,6 +130,11 @@ struct TextureInfo {
|
|||
static uint32_t TiledOffset2DInner(uint32_t x, uint32_t y, uint32_t bpp,
|
||||
uint32_t base_offset);
|
||||
|
||||
uint64_t hash() const;
|
||||
bool operator==(const TextureInfo& other) const {
|
||||
return std::memcmp(this, &other, sizeof(TextureInfo)) == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void CalculateTextureSizes1D(const xenos::xe_gpu_texture_fetch_t& fetch);
|
||||
void CalculateTextureSizes2D(const xenos::xe_gpu_texture_fetch_t& fetch);
|
||||
|
|
Loading…
Reference in New Issue