mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Add untested UpdateTextureLevels functionality to three of the four backends
This commit is contained in:
parent
eb872b7a87
commit
0b1abf1de9
5 changed files with 123 additions and 51 deletions
|
@ -92,6 +92,7 @@ public:
|
|||
Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) override;
|
||||
|
||||
void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override;
|
||||
void UpdateTextureLevels(Texture *texture, const uint8_t **data, int numLevels) override;
|
||||
|
||||
void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits, const char *tag) override;
|
||||
bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) override;
|
||||
|
@ -943,6 +944,11 @@ Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) {
|
|||
return tex;
|
||||
}
|
||||
|
||||
void D3D11DrawContext::UpdateTextureLevels(Texture *texture, const uint8_t **data, int numLevels) {
|
||||
D3D11Texture *tex = (D3D11Texture *)texture;
|
||||
// TODO
|
||||
}
|
||||
|
||||
ShaderModule *D3D11DrawContext::CreateShaderModule(ShaderStage stage, ShaderLanguage language, const uint8_t *data, size_t dataSize, const char *tag) {
|
||||
if (language != ShaderLanguage::HLSL_D3D11) {
|
||||
ERROR_LOG(G3D, "Unsupported shader language");
|
||||
|
|
|
@ -308,9 +308,10 @@ public:
|
|||
return nullptr;
|
||||
}
|
||||
}
|
||||
void UpdateTextureLevels(const uint8_t * const *data, int numLevels, TextureCallback initDataCallback);
|
||||
|
||||
private:
|
||||
void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback callback);
|
||||
void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback initDataCallback);
|
||||
bool Create(const TextureDesc &desc);
|
||||
LPDIRECT3DDEVICE9 device_;
|
||||
LPDIRECT3DDEVICE9EX deviceEx_;
|
||||
|
@ -374,27 +375,31 @@ bool D3D9Texture::Create(const TextureDesc &desc) {
|
|||
break;
|
||||
}
|
||||
if (FAILED(hr)) {
|
||||
ERROR_LOG(G3D, "Texture creation failed");
|
||||
ERROR_LOG(G3D, "D3D9 Texture creation failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (desc.initData.size()) {
|
||||
// In D3D9, after setting D3DUSAGE_AUTOGENMIPS, we can only access the top layer. The rest will be
|
||||
// automatically generated.
|
||||
int maxLevel = desc.generateMips ? 1 : (int)desc.initData.size();
|
||||
int w = desc.width;
|
||||
int h = desc.height;
|
||||
int d = desc.depth;
|
||||
for (int i = 0; i < maxLevel; i++) {
|
||||
SetImageData(0, 0, 0, w, h, d, i, 0, desc.initData[i], desc.initDataCallback);
|
||||
w = (w + 1) / 2;
|
||||
h = (h + 1) / 2;
|
||||
d = (d + 1) / 2;
|
||||
}
|
||||
int numLevels = desc.generateMips ? 1 : (int)desc.initData.size();
|
||||
UpdateTextureLevels(desc.initData.data(), numLevels, desc.initDataCallback);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void D3D9Texture::UpdateTextureLevels(const uint8_t * const *data, int numLevels, TextureCallback initDataCallback) {
|
||||
int w = width_;
|
||||
int h = height_;
|
||||
int d = depth_;
|
||||
for (int i = 0; i < numLevels; i++) {
|
||||
SetImageData(0, 0, 0, w, h, d, i, 0, data[i], initDataCallback);
|
||||
w = (w + 1) / 2;
|
||||
h = (h + 1) / 2;
|
||||
d = (d + 1) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Just switches R and G.
|
||||
inline uint32_t Shuffle8888(uint32_t x) {
|
||||
return (x & 0xFF00FF00) | ((x >> 16) & 0xFF) | ((x << 16) & 0xFF0000);
|
||||
|
@ -532,6 +537,7 @@ public:
|
|||
Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) override;
|
||||
|
||||
void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override;
|
||||
void UpdateTextureLevels(Texture *texture, const uint8_t **data, int numLevels) override;
|
||||
|
||||
void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits, const char *tag) override {
|
||||
// Not implemented
|
||||
|
@ -934,6 +940,12 @@ Texture *D3D9Context::CreateTexture(const TextureDesc &desc) {
|
|||
return tex;
|
||||
}
|
||||
|
||||
void D3D9Context::UpdateTextureLevels(Texture *texture, const uint8_t **data, int numLevels) {
|
||||
D3D9Texture *tex = (D3D9Texture *)texture;
|
||||
tex->UpdateTextureLevels(data, numLevels, TextureCallback());
|
||||
}
|
||||
|
||||
|
||||
void D3D9Context::BindTextures(int start, int count, Texture **textures, TextureBindFlags flags) {
|
||||
_assert_(start + count <= MAX_BOUND_TEXTURES);
|
||||
for (int i = start; i < start + count; i++) {
|
||||
|
|
|
@ -371,6 +371,7 @@ public:
|
|||
void EndFrame() override;
|
||||
|
||||
void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override;
|
||||
void UpdateTextureLevels(Texture *texture, const uint8_t **data, int numLevels) override;
|
||||
|
||||
void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits, const char *tag) override;
|
||||
bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) override;
|
||||
|
@ -853,8 +854,10 @@ public:
|
|||
return tex_;
|
||||
}
|
||||
|
||||
void UpdateTextureLevels(GLRenderManager *render, const uint8_t *const *data, int numLevels, TextureCallback initDataCallback);
|
||||
|
||||
private:
|
||||
void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback callback);
|
||||
void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback initDataCallback);
|
||||
|
||||
GLRenderManager *render_;
|
||||
GLRTexture *tex_;
|
||||
|
@ -862,16 +865,19 @@ private:
|
|||
DataFormat format_;
|
||||
TextureType type_;
|
||||
int mipLevels_;
|
||||
bool generatedMips_;
|
||||
bool generateMips_; // Generate mips requested
|
||||
bool generatedMips_; // Has generated mips
|
||||
};
|
||||
|
||||
OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) : render_(render) {
|
||||
generatedMips_ = false;
|
||||
generateMips_ = desc.generateMips;
|
||||
width_ = desc.width;
|
||||
height_ = desc.height;
|
||||
depth_ = desc.depth;
|
||||
format_ = desc.format;
|
||||
type_ = desc.type;
|
||||
|
||||
GLenum target = TypeToTarget(desc.type);
|
||||
tex_ = render->CreateTexture(target, desc.width, desc.height, 1, desc.mipLevels);
|
||||
|
||||
|
@ -879,21 +885,25 @@ OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) :
|
|||
if (desc.initData.empty())
|
||||
return;
|
||||
|
||||
UpdateTextureLevels(render, desc.initData.data(), (int)desc.initData.size(), desc.initDataCallback);
|
||||
}
|
||||
|
||||
void OpenGLTexture::UpdateTextureLevels(GLRenderManager *render, const uint8_t * const *data, int numLevels, TextureCallback initDataCallback) {
|
||||
int level = 0;
|
||||
int width = width_;
|
||||
int height = height_;
|
||||
int depth = depth_;
|
||||
for (auto data : desc.initData) {
|
||||
SetImageData(0, 0, 0, width, height, depth, level, 0, data, desc.initDataCallback);
|
||||
for (int i = 0; i < numLevels; i++) {
|
||||
SetImageData(0, 0, 0, width, height, depth, level, 0, data[i], initDataCallback);
|
||||
width = (width + 1) / 2;
|
||||
height = (height + 1) / 2;
|
||||
depth = (depth + 1) / 2;
|
||||
level++;
|
||||
}
|
||||
mipLevels_ = desc.generateMips ? desc.mipLevels : level;
|
||||
mipLevels_ = generateMips_ ? mipLevels_ : level;
|
||||
|
||||
bool genMips = false;
|
||||
if ((int)desc.initData.size() < desc.mipLevels && desc.generateMips) {
|
||||
if (numLevels < mipLevels_ && generateMips_) {
|
||||
// Assumes the texture is bound for editing
|
||||
genMips = true;
|
||||
generatedMips_ = true;
|
||||
|
@ -923,7 +933,7 @@ public:
|
|||
GLRFramebuffer *framebuffer_ = nullptr;
|
||||
};
|
||||
|
||||
void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback callback) {
|
||||
void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback initDataCallback) {
|
||||
if ((width != width_ || height != height_ || depth != depth_) && level == 0) {
|
||||
// When switching to texStorage we need to handle this correctly.
|
||||
width_ = width;
|
||||
|
@ -939,8 +949,8 @@ void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int
|
|||
uint8_t *texData = new uint8_t[(size_t)(width * height * depth * alignment)];
|
||||
|
||||
bool texDataPopulated = false;
|
||||
if (callback) {
|
||||
texDataPopulated = callback(texData, data, width, height, depth, width * (int)alignment, height * width * (int)alignment);
|
||||
if (initDataCallback) {
|
||||
texDataPopulated = initDataCallback(texData, data, width, height, depth, width * (int)alignment, height * width * (int)alignment);
|
||||
}
|
||||
if (texDataPopulated) {
|
||||
if (format_ == DataFormat::A1R5G5B5_UNORM_PACK16) {
|
||||
|
@ -1021,6 +1031,11 @@ Texture *OpenGLContext::CreateTexture(const TextureDesc &desc) {
|
|||
return new OpenGLTexture(&renderManager_, desc);
|
||||
}
|
||||
|
||||
void OpenGLContext::UpdateTextureLevels(Texture *texture, const uint8_t **data, int numLevels) {
|
||||
OpenGLTexture *tex = (OpenGLTexture *)texture;
|
||||
tex->UpdateTextureLevels(&renderManager_, data, numLevels, TextureCallback());
|
||||
}
|
||||
|
||||
DepthStencilState *OpenGLContext::CreateDepthStencilState(const DepthStencilStateDesc &desc) {
|
||||
OpenGLDepthStencilState *ds = new OpenGLDepthStencilState();
|
||||
ds->depthTestEnabled = desc.depthTestEnabled;
|
||||
|
|
|
@ -337,6 +337,7 @@ public:
|
|||
VKTexture(VulkanContext *vulkan, VkCommandBuffer cmd, VulkanPushPool *pushBuffer, const TextureDesc &desc)
|
||||
: vulkan_(vulkan), mipLevels_(desc.mipLevels), format_(desc.format) {}
|
||||
bool Create(VkCommandBuffer cmd, VulkanPushPool *pushBuffer, const TextureDesc &desc);
|
||||
void Update(VkCommandBuffer cmd, VulkanPushPool *pushBuffer, const uint8_t *const *data, TextureCallback callback, int numLevels);
|
||||
|
||||
~VKTexture() {
|
||||
Destroy();
|
||||
|
@ -356,7 +357,13 @@ public:
|
|||
return VK_NULL_HANDLE; // This would be bad.
|
||||
}
|
||||
|
||||
int NumLevels() const {
|
||||
return mipLevels_;
|
||||
}
|
||||
|
||||
private:
|
||||
void UpdateInternal(VkCommandBuffer cmd, VulkanPushPool *pushBuffer, const uint8_t *const *data, TextureCallback callback, int numLevels);
|
||||
|
||||
void Destroy() {
|
||||
if (vkTex_) {
|
||||
vkTex_->Destroy();
|
||||
|
@ -421,6 +428,7 @@ public:
|
|||
Framebuffer *CreateFramebuffer(const FramebufferDesc &desc) override;
|
||||
|
||||
void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) override;
|
||||
void UpdateTextureLevels(Texture *texture, const uint8_t **data, int numLevels) override;
|
||||
|
||||
void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits, const char *tag) override;
|
||||
bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) override;
|
||||
|
@ -748,14 +756,14 @@ enum class TextureState {
|
|||
PENDING_DESTRUCTION,
|
||||
};
|
||||
|
||||
bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushPool *push, const TextureDesc &desc) {
|
||||
bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushPool *pushBuffer, const TextureDesc &desc) {
|
||||
// Zero-sized textures not allowed.
|
||||
_assert_(desc.width * desc.height * desc.depth > 0); // remember to set depth to 1!
|
||||
if (desc.width * desc.height * desc.depth <= 0) {
|
||||
ERROR_LOG(G3D, "Bad texture dimensions %dx%dx%d", desc.width, desc.height, desc.depth);
|
||||
return false;
|
||||
}
|
||||
_assert_(push);
|
||||
_dbg_assert_(pushBuffer);
|
||||
format_ = desc.format;
|
||||
mipLevels_ = desc.mipLevels;
|
||||
width_ = desc.width;
|
||||
|
@ -763,8 +771,6 @@ bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushPool *push, const TextureD
|
|||
depth_ = desc.depth;
|
||||
vkTex_ = new VulkanTexture(vulkan_, desc.tag);
|
||||
VkFormat vulkanFormat = DataFormatToVulkan(format_);
|
||||
int bpp = GetBpp(vulkanFormat);
|
||||
int bytesPerPixel = bpp / 8;
|
||||
int usageBits = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
if (mipLevels_ > (int)desc.initData.size()) {
|
||||
// Gonna have to generate some, which requires TRANSFER_SRC
|
||||
|
@ -779,33 +785,10 @@ bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushPool *push, const TextureD
|
|||
}
|
||||
VkImageLayout layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
if (desc.initData.size()) {
|
||||
int w = width_;
|
||||
int h = height_;
|
||||
int d = depth_;
|
||||
int i;
|
||||
for (i = 0; i < (int)desc.initData.size(); i++) {
|
||||
uint32_t offset;
|
||||
VkBuffer buf;
|
||||
size_t size = w * h * d * bytesPerPixel;
|
||||
uint8_t *dest = (uint8_t *)push->Allocate(size, 16, &buf, &offset);
|
||||
if (desc.initDataCallback) {
|
||||
_assert_(dest != nullptr);
|
||||
if (!desc.initDataCallback(dest, desc.initData[i], w, h, d, w * bytesPerPixel, h * w * bytesPerPixel)) {
|
||||
memcpy(dest, desc.initData[i], size);
|
||||
}
|
||||
} else {
|
||||
memcpy(dest, desc.initData[i], size);
|
||||
}
|
||||
TextureCopyBatch batch;
|
||||
vkTex_->CopyBufferToMipLevel(cmd, &batch, i, w, h, 0, buf, offset, w);
|
||||
vkTex_->FinishCopyBatch(cmd, &batch);
|
||||
w = (w + 1) / 2;
|
||||
h = (h + 1) / 2;
|
||||
d = (d + 1) / 2;
|
||||
}
|
||||
UpdateInternal(cmd, pushBuffer, desc.initData.data(), desc.initDataCallback, (int)desc.initData.size());
|
||||
// Generate the rest of the mips automatically.
|
||||
if (i < mipLevels_) {
|
||||
vkTex_->GenerateMips(cmd, i, false);
|
||||
if (desc.initData.size() < mipLevels_) {
|
||||
vkTex_->GenerateMips(cmd, desc.initData.size(), false);
|
||||
layout = VK_IMAGE_LAYOUT_GENERAL;
|
||||
}
|
||||
}
|
||||
|
@ -813,6 +796,43 @@ bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushPool *push, const TextureD
|
|||
return true;
|
||||
}
|
||||
|
||||
void VKTexture::Update(VkCommandBuffer cmd, VulkanPushPool *pushBuffer, const uint8_t * const *data, TextureCallback initDataCallback, int numLevels) {
|
||||
// Before we can use UpdateInternal, we need to transition the image to the same state as after CreateDirect,
|
||||
// making it ready for writing.
|
||||
|
||||
UpdateInternal(cmd, pushBuffer, data, initDataCallback, numLevels);
|
||||
}
|
||||
|
||||
void VKTexture::UpdateInternal(VkCommandBuffer cmd, VulkanPushPool *pushBuffer, const uint8_t * const *data, TextureCallback initDataCallback, int numLevels) {
|
||||
int w = width_;
|
||||
int h = height_;
|
||||
int d = depth_;
|
||||
int i;
|
||||
VkFormat vulkanFormat = DataFormatToVulkan(format_);
|
||||
int bpp = GetBpp(vulkanFormat);
|
||||
int bytesPerPixel = bpp / 8;
|
||||
TextureCopyBatch batch;
|
||||
for (i = 0; i < numLevels; i++) {
|
||||
uint32_t offset;
|
||||
VkBuffer buf;
|
||||
size_t size = w * h * d * bytesPerPixel;
|
||||
uint8_t *dest = (uint8_t *)pushBuffer->Allocate(size, 16, &buf, &offset);
|
||||
if (initDataCallback) {
|
||||
_assert_(dest != nullptr);
|
||||
if (!initDataCallback(dest, data[i], w, h, d, w * bytesPerPixel, h * w * bytesPerPixel)) {
|
||||
memcpy(dest, data[i], size);
|
||||
}
|
||||
} else {
|
||||
memcpy(dest, data[i], size);
|
||||
}
|
||||
vkTex_->CopyBufferToMipLevel(cmd, &batch, i, w, h, 0, buf, offset, w);
|
||||
w = (w + 1) / 2;
|
||||
h = (h + 1) / 2;
|
||||
d = (d + 1) / 2;
|
||||
}
|
||||
vkTex_->FinishCopyBatch(cmd, &batch);
|
||||
}
|
||||
|
||||
static DataFormat DataFormatFromVulkanDepth(VkFormat fmt) {
|
||||
switch (fmt) {
|
||||
case VK_FORMAT_D24_UNORM_S8_UINT:
|
||||
|
@ -1342,6 +1362,20 @@ Texture *VKContext::CreateTexture(const TextureDesc &desc) {
|
|||
}
|
||||
}
|
||||
|
||||
void VKContext::UpdateTextureLevels(Texture *texture, const uint8_t **data, int numLevels) {
|
||||
VkCommandBuffer initCmd = renderManager_.GetInitCmd();
|
||||
if (!push_ || !initCmd) {
|
||||
// Too early! Fail.
|
||||
ERROR_LOG(G3D, "Can't create textures before the first frame has started.");
|
||||
return;
|
||||
}
|
||||
|
||||
VKTexture *tex = (VKTexture *)texture;
|
||||
|
||||
_dbg_assert_(numLevels <= tex->NumLevels());
|
||||
tex->Update(initCmd, push_, data, Draw::TextureCallback(), numLevels);
|
||||
}
|
||||
|
||||
static inline void CopySide(VkStencilOpState &dest, const StencilSetup &src) {
|
||||
dest.compareOp = compToVK[(int)src.compareOp];
|
||||
dest.failOp = stencilOpToVK[(int)src.failOp];
|
||||
|
|
|
@ -731,6 +731,11 @@ public:
|
|||
// Copies data from the CPU over into the buffer, at a specific offset. This does not change the size of the buffer and cannot write outside it.
|
||||
virtual void UpdateBuffer(Buffer *buffer, const uint8_t *data, size_t offset, size_t size, UpdateBufferFlags flags) = 0;
|
||||
|
||||
// Used to optimize DrawPixels by re-using previously allocated temp textures.
|
||||
// Do not try to update a texture that might be used by an in-flight command buffer! In OpenGL and D3D, this will cause stalls
|
||||
// while in Vulkan this might cause various strangeness like image corruption.
|
||||
virtual void UpdateTextureLevels(Texture *texture, const uint8_t **data, int numLevels) = 0;
|
||||
|
||||
virtual void CopyFramebufferImage(Framebuffer *src, int level, int x, int y, int z, Framebuffer *dst, int dstLevel, int dstX, int dstY, int dstZ, int width, int height, int depth, int channelBits, const char *tag) = 0;
|
||||
virtual bool BlitFramebuffer(Framebuffer *src, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dst, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) = 0;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue