summaryrefslogtreecommitdiffstats
path: root/src/video_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core')
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp17
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp7
-rw-r--r--src/video_core/texture_cache/image_base.h3
-rw-r--r--src/video_core/texture_cache/texture_cache.h70
-rw-r--r--src/video_core/texture_cache/texture_cache_base.h16
-rw-r--r--src/video_core/textures/astc.cpp4
6 files changed, 109 insertions, 8 deletions
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index eb6e43a08..b047e7b3d 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -228,8 +228,9 @@ void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4
[[nodiscard]] bool CanBeAccelerated(const TextureCacheRuntime& runtime,
const VideoCommon::ImageInfo& info) {
- if (IsPixelFormatASTC(info.format)) {
- return !runtime.HasNativeASTC() && Settings::values.accelerate_astc.GetValue();
+ if (IsPixelFormatASTC(info.format) && !runtime.HasNativeASTC()) {
+ return Settings::values.accelerate_astc.GetValue() &&
+ !Settings::values.async_astc.GetValue();
}
// Disable other accelerated uploads for now as they don't implement swizzled uploads
return false;
@@ -258,6 +259,14 @@ void ApplySwizzle(GLuint handle, PixelFormat format, std::array<SwizzleSource, 4
return format_info.compatibility_class == store_class;
}
+[[nodiscard]] bool CanBeDecodedAsync(const TextureCacheRuntime& runtime,
+ const VideoCommon::ImageInfo& info) {
+ if (IsPixelFormatASTC(info.format) && !runtime.HasNativeASTC()) {
+ return Settings::values.async_astc.GetValue();
+ }
+ return false;
+}
+
[[nodiscard]] CopyOrigin MakeCopyOrigin(VideoCommon::Offset3D offset,
VideoCommon::SubresourceLayers subresource, GLenum target) {
switch (target) {
@@ -721,7 +730,9 @@ std::optional<size_t> TextureCacheRuntime::StagingBuffers::FindBuffer(size_t req
Image::Image(TextureCacheRuntime& runtime_, const VideoCommon::ImageInfo& info_, GPUVAddr gpu_addr_,
VAddr cpu_addr_)
: VideoCommon::ImageBase(info_, gpu_addr_, cpu_addr_), runtime{&runtime_} {
- if (CanBeAccelerated(*runtime, info)) {
+ if (CanBeDecodedAsync(*runtime, info)) {
+ flags |= ImageFlagBits::AsynchronousDecode;
+ } else if (CanBeAccelerated(*runtime, info)) {
flags |= ImageFlagBits::AcceleratedUpload;
}
if (IsConverted(runtime->device, info.format, info.type)) {
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 9b85dfb5e..80adb70eb 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -1256,11 +1256,12 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
commit(runtime_.memory_allocator.Commit(original_image, MemoryUsage::DeviceLocal)),
aspect_mask(ImageAspectMask(info.format)) {
if (IsPixelFormatASTC(info.format) && !runtime->device.IsOptimalAstcSupported()) {
- if (Settings::values.accelerate_astc.GetValue()) {
+ if (Settings::values.async_astc.GetValue()) {
+ flags |= VideoCommon::ImageFlagBits::AsynchronousDecode;
+ } else if (Settings::values.accelerate_astc.GetValue()) {
flags |= VideoCommon::ImageFlagBits::AcceleratedUpload;
- } else {
- flags |= VideoCommon::ImageFlagBits::Converted;
}
+ flags |= VideoCommon::ImageFlagBits::Converted;
flags |= VideoCommon::ImageFlagBits::CostlyLoad;
}
if (runtime->device.HasDebuggingToolAttached()) {
diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h
index 620565684..e8fa592d2 100644
--- a/src/video_core/texture_cache/image_base.h
+++ b/src/video_core/texture_cache/image_base.h
@@ -38,6 +38,9 @@ enum class ImageFlagBits : u32 {
Rescaled = 1 << 13,
CheckingRescalable = 1 << 14,
IsRescalable = 1 << 15,
+
+ AsynchronousDecode = 1 << 16,
+ IsDecoding = 1 << 17, ///< Is currently being decoded asynchornously.
};
DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits)
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 3e2cbb0b0..9dd152fbe 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -85,6 +85,11 @@ void TextureCache<P>::RunGarbageCollector() {
}
--num_iterations;
auto& image = slot_images[image_id];
+ if (True(image.flags & ImageFlagBits::IsDecoding)) {
+ // This image is still being decoded, deleting it will invalidate the slot
+ // used by the async decoder thread.
+ return false;
+ }
const bool must_download =
image.IsSafeDownload() && False(image.flags & ImageFlagBits::BadOverlap);
if (!high_priority_mode &&
@@ -133,6 +138,8 @@ void TextureCache<P>::TickFrame() {
sentenced_images.Tick();
sentenced_framebuffers.Tick();
sentenced_image_view.Tick();
+ TickAsyncDecode();
+
runtime.TickFrame();
critical_gc = 0;
++frame_tick;
@@ -777,6 +784,10 @@ void TextureCache<P>::RefreshContents(Image& image, ImageId image_id) {
LOG_WARNING(HW_GPU, "MSAA image uploads are not implemented");
return;
}
+ if (True(image.flags & ImageFlagBits::AsynchronousDecode)) {
+ QueueAsyncDecode(image, image_id);
+ return;
+ }
auto staging = runtime.UploadStagingBuffer(MapSizeBytes(image));
UploadImageContents(image, staging);
runtime.InsertUploadMemoryBarrier();
@@ -990,6 +1001,65 @@ u64 TextureCache<P>::GetScaledImageSizeBytes(const ImageBase& image) {
}
template <class P>
+void TextureCache<P>::QueueAsyncDecode(Image& image, ImageId image_id) {
+ UNIMPLEMENTED_IF(False(image.flags & ImageFlagBits::Converted));
+ LOG_INFO(HW_GPU, "Queuing async texture decode");
+
+ image.flags |= ImageFlagBits::IsDecoding;
+ auto decode = std::make_unique<AsyncDecodeContext>();
+ auto* decode_ptr = decode.get();
+ decode->image_id = image_id;
+ async_decodes.push_back(std::move(decode));
+
+ Common::ScratchBuffer<u8> local_unswizzle_data_buffer(image.unswizzled_size_bytes);
+ const size_t guest_size_bytes = image.guest_size_bytes;
+ swizzle_data_buffer.resize_destructive(guest_size_bytes);
+ gpu_memory->ReadBlockUnsafe(image.gpu_addr, swizzle_data_buffer.data(), guest_size_bytes);
+ auto copies = UnswizzleImage(*gpu_memory, image.gpu_addr, image.info, swizzle_data_buffer,
+ local_unswizzle_data_buffer);
+ const size_t out_size = MapSizeBytes(image);
+
+ auto func = [out_size, copies, info = image.info,
+ input = std::move(local_unswizzle_data_buffer),
+ async_decode = decode_ptr]() mutable {
+ async_decode->decoded_data.resize_destructive(out_size);
+ std::span copies_span{copies.data(), copies.size()};
+ ConvertImage(input, info, async_decode->decoded_data, copies_span);
+
+ // TODO: Do we need this lock?
+ std::unique_lock lock{async_decode->mutex};
+ async_decode->copies = std::move(copies);
+ async_decode->complete = true;
+ };
+ texture_decode_worker.QueueWork(std::move(func));
+}
+
+template <class P>
+void TextureCache<P>::TickAsyncDecode() {
+ bool has_uploads{};
+ auto i = async_decodes.begin();
+ while (i != async_decodes.end()) {
+ auto* async_decode = i->get();
+ std::unique_lock lock{async_decode->mutex};
+ if (!async_decode->complete) {
+ ++i;
+ continue;
+ }
+ Image& image = slot_images[async_decode->image_id];
+ auto staging = runtime.UploadStagingBuffer(MapSizeBytes(image));
+ std::memcpy(staging.mapped_span.data(), async_decode->decoded_data.data(),
+ async_decode->decoded_data.size());
+ image.UploadMemory(staging, async_decode->copies);
+ image.flags &= ~ImageFlagBits::IsDecoding;
+ has_uploads = true;
+ i = async_decodes.erase(i);
+ }
+ if (has_uploads) {
+ runtime.InsertUploadMemoryBarrier();
+ }
+}
+
+template <class P>
bool TextureCache<P>::ScaleUp(Image& image) {
const bool has_copy = image.HasScaled();
const bool rescaled = image.ScaleUp();
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index 485eaabaa..013836933 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -3,6 +3,7 @@
#pragma once
+#include <atomic>
#include <deque>
#include <limits>
#include <mutex>
@@ -18,6 +19,7 @@
#include "common/lru_cache.h"
#include "common/polyfill_ranges.h"
#include "common/scratch_buffer.h"
+#include "common/thread_worker.h"
#include "video_core/compatible_formats.h"
#include "video_core/control/channel_state_cache.h"
#include "video_core/delayed_destruction_ring.h"
@@ -54,6 +56,14 @@ struct ImageViewInOut {
ImageViewId id{};
};
+struct AsyncDecodeContext {
+ ImageId image_id;
+ Common::ScratchBuffer<u8> decoded_data;
+ std::vector<BufferImageCopy> copies;
+ std::mutex mutex;
+ std::atomic_bool complete;
+};
+
using TextureCacheGPUMap = std::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>>;
class TextureCacheChannelInfo : public ChannelInfo {
@@ -377,6 +387,9 @@ private:
bool ScaleDown(Image& image);
u64 GetScaledImageSizeBytes(const ImageBase& image);
+ void QueueAsyncDecode(Image& image, ImageId image_id);
+ void TickAsyncDecode();
+
Runtime& runtime;
VideoCore::RasterizerInterface& rasterizer;
@@ -430,6 +443,9 @@ private:
u64 modification_tick = 0;
u64 frame_tick = 0;
+
+ Common::ThreadWorker texture_decode_worker{1, "TextureDecoder"};
+ std::vector<std::unique_ptr<AsyncDecodeContext>> async_decodes;
};
} // namespace VideoCommon
diff --git a/src/video_core/textures/astc.cpp b/src/video_core/textures/astc.cpp
index e8d7c7863..4381eed1d 100644
--- a/src/video_core/textures/astc.cpp
+++ b/src/video_core/textures/astc.cpp
@@ -1656,8 +1656,8 @@ void Decompress(std::span<const uint8_t> data, uint32_t width, uint32_t height,
const u32 rows = Common::DivideUp(height, block_height);
const u32 cols = Common::DivideUp(width, block_width);
- Common::ThreadWorker workers{std::max(std::thread::hardware_concurrency(), 2U) / 2,
- "ASTCDecompress"};
+ static Common::ThreadWorker workers{std::max(std::thread::hardware_concurrency(), 2U) / 2,
+ "ASTCDecompress"};
for (u32 z = 0; z < depth; ++z) {
const u32 depth_offset = z * height * width * 4;