mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-25 07:09:48 -06:00
VideoBackends: Add AbstractStagingTexture class
Can be used for asynchronous readback or upload of textures.
This commit is contained in:
133
Source/Core/VideoCommon/AbstractStagingTexture.cpp
Normal file
133
Source/Core/VideoCommon/AbstractStagingTexture.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "VideoCommon/AbstractStagingTexture.h"
|
||||
#include "VideoCommon/AbstractTexture.h"
|
||||
|
||||
AbstractStagingTexture::AbstractStagingTexture(StagingTextureType type, const TextureConfig& c)
|
||||
: m_type(type), m_config(c), m_texel_size(AbstractTexture::GetTexelSizeForFormat(c.format))
|
||||
{
|
||||
}
|
||||
|
||||
AbstractStagingTexture::~AbstractStagingTexture() = default;
|
||||
|
||||
void AbstractStagingTexture::CopyFromTexture(const AbstractTexture* src, u32 src_layer,
|
||||
u32 src_level)
|
||||
{
|
||||
MathUtil::Rectangle<int> src_rect = src->GetConfig().GetMipRect(src_level);
|
||||
MathUtil::Rectangle<int> dst_rect = m_config.GetRect();
|
||||
CopyFromTexture(src, src_rect, src_layer, src_level, dst_rect);
|
||||
}
|
||||
|
||||
void AbstractStagingTexture::CopyToTexture(AbstractTexture* dst, u32 dst_layer, u32 dst_level)
|
||||
{
|
||||
MathUtil::Rectangle<int> src_rect = m_config.GetRect();
|
||||
MathUtil::Rectangle<int> dst_rect = dst->GetConfig().GetMipRect(dst_level);
|
||||
CopyToTexture(src_rect, dst, dst_rect, dst_layer, dst_level);
|
||||
}
|
||||
|
||||
void AbstractStagingTexture::ReadTexels(const MathUtil::Rectangle<int>& rect, void* out_ptr,
|
||||
u32 out_stride)
|
||||
{
|
||||
_assert_(m_type != StagingTextureType::Upload);
|
||||
if (!PrepareForAccess())
|
||||
return;
|
||||
|
||||
_assert_(rect.left >= 0 && static_cast<u32>(rect.right) <= m_config.width && rect.top >= 0 &&
|
||||
static_cast<u32>(rect.bottom) <= m_config.height);
|
||||
|
||||
// Offset pointer to point to start of region being copied out.
|
||||
const char* current_ptr = m_map_pointer;
|
||||
current_ptr += rect.top * m_map_stride;
|
||||
current_ptr += rect.left * m_texel_size;
|
||||
|
||||
// Optimal path: same dimensions, same stride.
|
||||
if (rect.left == 0 && static_cast<u32>(rect.right) == m_config.width &&
|
||||
m_map_stride == out_stride)
|
||||
{
|
||||
std::memcpy(out_ptr, current_ptr, m_map_stride * rect.GetHeight());
|
||||
return;
|
||||
}
|
||||
|
||||
size_t copy_size = std::min(static_cast<size_t>(rect.GetWidth() * m_texel_size), m_map_stride);
|
||||
int copy_height = rect.GetHeight();
|
||||
char* dst_ptr = reinterpret_cast<char*>(out_ptr);
|
||||
for (int row = 0; row < copy_height; row++)
|
||||
{
|
||||
std::memcpy(dst_ptr, current_ptr, copy_size);
|
||||
current_ptr += m_map_stride;
|
||||
dst_ptr += out_stride;
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractStagingTexture::ReadTexel(u32 x, u32 y, void* out_ptr)
|
||||
{
|
||||
_assert_(m_type != StagingTextureType::Upload);
|
||||
if (!PrepareForAccess())
|
||||
return;
|
||||
|
||||
_assert_(x < m_config.width && y < m_config.height);
|
||||
const char* src_ptr = m_map_pointer + y * m_map_stride + x * m_texel_size;
|
||||
std::memcpy(out_ptr, src_ptr, m_texel_size);
|
||||
}
|
||||
|
||||
void AbstractStagingTexture::WriteTexels(const MathUtil::Rectangle<int>& rect, const void* in_ptr,
|
||||
u32 in_stride)
|
||||
{
|
||||
_assert_(m_type != StagingTextureType::Readback);
|
||||
if (!PrepareForAccess())
|
||||
return;
|
||||
|
||||
_assert_(rect.left >= 0 && static_cast<u32>(rect.right) <= m_config.width && rect.top >= 0 &&
|
||||
static_cast<u32>(rect.bottom) <= m_config.height);
|
||||
|
||||
// Offset pointer to point to start of region being copied to.
|
||||
char* current_ptr = m_map_pointer;
|
||||
current_ptr += rect.top * m_map_stride;
|
||||
current_ptr += rect.left * m_texel_size;
|
||||
|
||||
// Optimal path: same dimensions, same stride.
|
||||
if (rect.left == 0 && static_cast<u32>(rect.right) == m_config.width && m_map_stride == in_stride)
|
||||
{
|
||||
std::memcpy(current_ptr, in_ptr, m_map_stride * rect.GetHeight());
|
||||
return;
|
||||
}
|
||||
|
||||
size_t copy_size = std::min(static_cast<size_t>(rect.GetWidth() * m_texel_size), m_map_stride);
|
||||
int copy_height = rect.GetHeight();
|
||||
const char* src_ptr = reinterpret_cast<const char*>(in_ptr);
|
||||
for (int row = 0; row < copy_height; row++)
|
||||
{
|
||||
std::memcpy(current_ptr, src_ptr, copy_size);
|
||||
current_ptr += m_map_stride;
|
||||
src_ptr += in_stride;
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractStagingTexture::WriteTexel(u32 x, u32 y, const void* in_ptr)
|
||||
{
|
||||
_assert_(m_type != StagingTextureType::Readback);
|
||||
if (!PrepareForAccess())
|
||||
return;
|
||||
|
||||
_assert_(x < m_config.width && y < m_config.height);
|
||||
char* dest_ptr = m_map_pointer + y * m_map_stride + x * m_texel_size;
|
||||
std::memcpy(dest_ptr, in_ptr, m_texel_size);
|
||||
}
|
||||
|
||||
bool AbstractStagingTexture::PrepareForAccess()
|
||||
{
|
||||
if (m_needs_flush)
|
||||
{
|
||||
if (IsMapped())
|
||||
Unmap();
|
||||
Flush();
|
||||
}
|
||||
return IsMapped() || Map();
|
||||
}
|
86
Source/Core/VideoCommon/AbstractStagingTexture.h
Normal file
86
Source/Core/VideoCommon/AbstractStagingTexture.h
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/MathUtil.h"
|
||||
#include "VideoCommon/TextureConfig.h"
|
||||
|
||||
class AbstractTexture;
|
||||
|
||||
class AbstractStagingTexture
|
||||
{
|
||||
public:
|
||||
explicit AbstractStagingTexture(StagingTextureType type, const TextureConfig& c);
|
||||
virtual ~AbstractStagingTexture();
|
||||
|
||||
const TextureConfig& GetConfig() const { return m_config; }
|
||||
StagingTextureType GetType() const { return m_type; }
|
||||
size_t GetTexelSize() const { return m_texel_size; }
|
||||
bool IsMapped() const { return m_map_pointer != nullptr; }
|
||||
char* GetMappedPointer() const { return m_map_pointer; }
|
||||
size_t GetMappedStride() const { return m_map_stride; }
|
||||
// Copies from the GPU texture object to the staging texture, which can be mapped/read by the CPU.
|
||||
// Both src_rect and dst_rect must be with within the bounds of the the specified textures.
|
||||
virtual void CopyFromTexture(const AbstractTexture* src, const MathUtil::Rectangle<int>& src_rect,
|
||||
u32 src_layer, u32 src_level,
|
||||
const MathUtil::Rectangle<int>& dst_rect) = 0;
|
||||
|
||||
// Wrapper for copying a whole layer of a texture to a readback texture.
|
||||
// Assumes that the level of src texture and this texture have the same dimensions.
|
||||
void CopyFromTexture(const AbstractTexture* src, u32 src_layer = 0, u32 src_level = 0);
|
||||
|
||||
// Copies from this staging texture to a GPU texture.
|
||||
// Both src_rect and dst_rect must be with within the bounds of the the specified textures.
|
||||
virtual void CopyToTexture(const MathUtil::Rectangle<int>& src_rect, AbstractTexture* dst,
|
||||
const MathUtil::Rectangle<int>& dst_rect, u32 dst_layer,
|
||||
u32 dst_level) = 0;
|
||||
|
||||
// Wrapper for copying a whole layer of a texture to a readback texture.
|
||||
// Assumes that the level of src texture and this texture have the same dimensions.
|
||||
void CopyToTexture(AbstractTexture* dst, u32 dst_layer = 0, u32 dst_level = 0);
|
||||
|
||||
// Maps the texture into the CPU address space, enabling it to read the contents.
|
||||
// The Map call may not perform synchronization. If the contents of the staging texture
|
||||
// has been updated by a CopyFromTexture call, you must call Flush() first.
|
||||
// If persistent mapping is supported in the backend, this may be a no-op.
|
||||
virtual bool Map() = 0;
|
||||
|
||||
// Unmaps the CPU-readable copy of the texture. May be a no-op on backends which
|
||||
// support persistent-mapped buffers.
|
||||
virtual void Unmap() = 0;
|
||||
|
||||
// Flushes pending writes from the CPU to the GPU, and reads from the GPU to the CPU.
|
||||
// This may cause a command buffer flush depending on if one has occurred between the last
|
||||
// call to CopyFromTexture()/CopyToTexture() and the Flush() call.
|
||||
virtual void Flush() = 0;
|
||||
|
||||
// Reads the specified rectangle from the staging texture to out_ptr, with the specified stride
|
||||
// (length in bytes of each row). CopyFromTexture must be called first. The contents of any
|
||||
// texels outside of the rectangle used for CopyFromTexture is undefined.
|
||||
void ReadTexels(const MathUtil::Rectangle<int>& rect, void* out_ptr, u32 out_stride);
|
||||
void ReadTexel(u32 x, u32 y, void* out_ptr);
|
||||
|
||||
// Copies the texels from in_ptr to the staging texture, which can be read by the GPU, with the
|
||||
// specified stride (length in bytes of each row). After updating the staging texture with all
|
||||
// changes, call CopyToTexture() to update the GPU copy.
|
||||
void WriteTexels(const MathUtil::Rectangle<int>& rect, const void* in_ptr, u32 in_stride);
|
||||
void WriteTexel(u32 x, u32 y, const void* in_ptr);
|
||||
|
||||
protected:
|
||||
bool PrepareForAccess();
|
||||
|
||||
const StagingTextureType m_type;
|
||||
const TextureConfig m_config;
|
||||
const size_t m_texel_size;
|
||||
|
||||
char* m_map_pointer = nullptr;
|
||||
size_t m_map_stride = 0;
|
||||
|
||||
bool m_needs_flush = false;
|
||||
};
|
@ -100,7 +100,7 @@ AbstractTexture::MapRegionImpl(u32 level, u32 x, u32 y, u32 width, u32 height)
|
||||
return {};
|
||||
}
|
||||
|
||||
bool AbstractTexture::IsCompressedHostTextureFormat(AbstractTextureFormat format)
|
||||
bool AbstractTexture::IsCompressedFormat(AbstractTextureFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
@ -115,7 +115,7 @@ bool AbstractTexture::IsCompressedHostTextureFormat(AbstractTextureFormat format
|
||||
}
|
||||
}
|
||||
|
||||
size_t AbstractTexture::CalculateHostTextureLevelPitch(AbstractTextureFormat format, u32 row_length)
|
||||
size_t AbstractTexture::CalculateStrideForFormat(AbstractTextureFormat format, u32 row_length)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
@ -134,6 +134,25 @@ size_t AbstractTexture::CalculateHostTextureLevelPitch(AbstractTextureFormat for
|
||||
}
|
||||
}
|
||||
|
||||
size_t AbstractTexture::GetTexelSizeForFormat(AbstractTextureFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case AbstractTextureFormat::DXT1:
|
||||
return 8;
|
||||
case AbstractTextureFormat::DXT3:
|
||||
case AbstractTextureFormat::DXT5:
|
||||
case AbstractTextureFormat::BPTC:
|
||||
return 16;
|
||||
case AbstractTextureFormat::RGBA8:
|
||||
case AbstractTextureFormat::BGRA8:
|
||||
return 4;
|
||||
default:
|
||||
PanicAlert("Unhandled texture format.");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const TextureConfig& AbstractTexture::GetConfig() const
|
||||
{
|
||||
return m_config;
|
||||
|
@ -39,8 +39,9 @@ public:
|
||||
virtual void Load(u32 level, u32 width, u32 height, u32 row_length, const u8* buffer,
|
||||
size_t buffer_size) = 0;
|
||||
|
||||
static bool IsCompressedHostTextureFormat(AbstractTextureFormat format);
|
||||
static size_t CalculateHostTextureLevelPitch(AbstractTextureFormat format, u32 row_length);
|
||||
static bool IsCompressedFormat(AbstractTextureFormat format);
|
||||
static size_t CalculateStrideForFormat(AbstractTextureFormat format, u32 row_length);
|
||||
static size_t GetTexelSizeForFormat(AbstractTextureFormat format);
|
||||
|
||||
const TextureConfig& GetConfig() const;
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
set(SRCS
|
||||
AbstractStagingTexture.cpp
|
||||
AbstractTexture.cpp
|
||||
AsyncRequests.cpp
|
||||
AsyncShaderCompiler.cpp
|
||||
|
@ -34,9 +34,11 @@
|
||||
|
||||
class AbstractRawTexture;
|
||||
class AbstractTexture;
|
||||
class AbstractStagingTexture;
|
||||
class PostProcessingShaderImplementation;
|
||||
struct TextureConfig;
|
||||
enum class EFBAccessType;
|
||||
enum class StagingTextureType;
|
||||
|
||||
struct EfbPokeData
|
||||
{
|
||||
@ -81,6 +83,8 @@ public:
|
||||
virtual void ResetAPIState() {}
|
||||
virtual void RestoreAPIState() {}
|
||||
virtual std::unique_ptr<AbstractTexture> CreateTexture(const TextureConfig& config) = 0;
|
||||
virtual std::unique_ptr<AbstractStagingTexture>
|
||||
CreateStagingTexture(StagingTextureType type, const TextureConfig& config) = 0;
|
||||
|
||||
// Ideal internal resolution - multiple of the native EFB resolution
|
||||
int GetTargetWidth() const { return m_target_width; }
|
||||
|
@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "VideoCommon/TextureConfig.h"
|
||||
#include "VideoCommon/AbstractTexture.h"
|
||||
|
||||
#include <tuple>
|
||||
|
||||
@ -12,7 +13,28 @@ bool TextureConfig::operator==(const TextureConfig& o) const
|
||||
std::tie(o.width, o.height, o.levels, o.layers, o.format, o.rendertarget);
|
||||
}
|
||||
|
||||
bool TextureConfig::operator!=(const TextureConfig& o) const
|
||||
{
|
||||
return !operator==(o);
|
||||
}
|
||||
|
||||
MathUtil::Rectangle<int> TextureConfig::GetRect() const
|
||||
{
|
||||
return {0, 0, static_cast<int>(width), static_cast<int>(height)};
|
||||
}
|
||||
|
||||
MathUtil::Rectangle<int> TextureConfig::GetMipRect(u32 level) const
|
||||
{
|
||||
return {0, 0, static_cast<int>(std::max(width >> level, 1u)),
|
||||
static_cast<int>(std::max(height >> level, 1u))};
|
||||
}
|
||||
|
||||
size_t TextureConfig::GetStride() const
|
||||
{
|
||||
return AbstractTexture::CalculateStrideForFormat(format, width);
|
||||
}
|
||||
|
||||
size_t TextureConfig::GetMipStride(u32 level) const
|
||||
{
|
||||
return AbstractTexture::CalculateStrideForFormat(format, std::max(width >> level, 1u));
|
||||
}
|
||||
|
@ -21,6 +21,13 @@ enum class AbstractTextureFormat : u32
|
||||
Undefined
|
||||
};
|
||||
|
||||
enum class StagingTextureType
|
||||
{
|
||||
Readback, // Optimize for CPU reads, GPU writes, no CPU writes
|
||||
Upload, // Optimize for CPU writes, GPU reads, no CPU reads
|
||||
Mutable // Optimize for CPU reads, GPU writes, allow slow CPU reads
|
||||
};
|
||||
|
||||
struct TextureConfig
|
||||
{
|
||||
constexpr TextureConfig() = default;
|
||||
@ -32,7 +39,11 @@ struct TextureConfig
|
||||
}
|
||||
|
||||
bool operator==(const TextureConfig& o) const;
|
||||
bool operator!=(const TextureConfig& o) const;
|
||||
MathUtil::Rectangle<int> GetRect() const;
|
||||
MathUtil::Rectangle<int> GetMipRect(u32 level) const;
|
||||
size_t GetStride() const;
|
||||
size_t GetMipStride(u32 level) const;
|
||||
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
|
@ -36,6 +36,7 @@
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AbstractStagingTexture.cpp" />
|
||||
<ClCompile Include="AbstractTexture.cpp" />
|
||||
<ClCompile Include="AsyncRequests.cpp" />
|
||||
<ClCompile Include="AsyncShaderCompiler.cpp" />
|
||||
@ -96,6 +97,7 @@
|
||||
<ClCompile Include="XFStructs.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="AbstractStagingTexture.h" />
|
||||
<ClInclude Include="AbstractTexture.h" />
|
||||
<ClInclude Include="AsyncRequests.h" />
|
||||
<ClInclude Include="AsyncShaderCompiler.h" />
|
||||
|
@ -191,6 +191,9 @@
|
||||
<ClCompile Include="UberShaderVertex.cpp">
|
||||
<Filter>Shader Generators</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AbstractStagingTexture.cpp">
|
||||
<Filter>Base</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="CommandProcessor.h" />
|
||||
@ -362,6 +365,9 @@
|
||||
<ClInclude Include="UberShaderVertex.h">
|
||||
<Filter>Shader Generators</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AbstractStagingTexture.h">
|
||||
<Filter>Base</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="CMakeLists.txt" />
|
||||
|
Reference in New Issue
Block a user