GPU: Use a texture directly for MakePixelTexture.

This makes it easier to do things with it.
This commit is contained in:
Unknown W. Brackets 2020-05-11 09:47:26 -07:00
parent 2653e50200
commit 762b656ea2
20 changed files with 317 additions and 315 deletions

View file

@ -662,11 +662,16 @@ void FramebufferManagerCommon::DrawPixels(VirtualFramebuffer *vfb, int dstX, int
draw_->SetScissorRect(0, 0, pixelWidth_, pixelHeight_);
}
MakePixelTexture(srcPixels, srcPixelFormat, srcStride, width, height, u1, v1);
Draw::Texture *pixelsTex = MakePixelTexture(srcPixels, srcPixelFormat, srcStride, width, height, u1, v1);
if (pixelsTex) {
draw_->BindTextures(0, 1, &pixelsTex);
Bind2DShader();
DrawActiveTexture(dstX, dstY, width, height, vfb->bufferWidth, vfb->bufferHeight, u0, v0, u1, v1, ROTATION_LOCKED_HORIZONTAL, flags);
gpuStats.numUploads++;
Bind2DShader();
DrawActiveTexture(dstX, dstY, width, height, vfb->bufferWidth, vfb->bufferHeight, u0, v0, u1, v1, ROTATION_LOCKED_HORIZONTAL, flags);
gpuStats.numUploads++;
pixelsTex->Release();
}
}
void FramebufferManagerCommon::CopyFramebufferForColorTexture(VirtualFramebuffer *dst, VirtualFramebuffer *src, int flags) {
@ -701,7 +706,10 @@ void FramebufferManagerCommon::DrawFramebufferToOutput(const u8 *srcPixels, GEBu
float u0 = 0.0f, u1 = 480.0f / 512.0f;
float v0 = 0.0f, v1 = 1.0f;
MakePixelTexture(srcPixels, srcPixelFormat, srcStride, 512, 272, u1, v1);
Draw::Texture *pixelsTex = MakePixelTexture(srcPixels, srcPixelFormat, srcStride, 512, 272, u1, v1);
if (!pixelsTex)
return;
draw_->BindTextures(0, 1, &pixelsTex);
int uvRotation = useBufferedRendering_ ? g_Config.iInternalScreenRotation : ROTATION_LOCKED_HORIZONTAL;
// TODO: Currently we can't access the texture from Draw.
@ -712,6 +720,9 @@ void FramebufferManagerCommon::DrawFramebufferToOutput(const u8 *srcPixels, GEBu
Bind2DShader();
if (needBackBufferYSwap_)
std::swap(v0, v1);
DrawTextureFlags flags = g_Config.iBufFilter == SCALE_LINEAR ? DRAWTEX_LINEAR : DRAWTEX_NEAREST;
flags = flags | DRAWTEX_TO_BACKBUFFER;
SetViewport2D(0, 0, pixelWidth_, pixelHeight_);
@ -735,6 +746,8 @@ void FramebufferManagerCommon::DrawFramebufferToOutput(const u8 *srcPixels, GEBu
presentation_->CopyToOutput(flags, uvRotation, u0, v0, u1, v1, uniforms);
}*/
pixelsTex->Release();
// PresentationCommon sets all kinds of state, we can't rely on anything.
gstate_c.Dirty(DIRTY_ALL);
}

View file

@ -138,7 +138,6 @@ enum BindFramebufferColorFlags {
enum DrawTextureFlags {
DRAWTEX_NEAREST = 0,
DRAWTEX_LINEAR = 1,
DRAWTEX_KEEP_TEX = 2,
DRAWTEX_KEEP_STENCIL_ALPHA = 4,
DRAWTEX_TO_BACKBUFFER = 8,
};
@ -309,7 +308,7 @@ public:
protected:
virtual void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h);
void SetViewport2D(int x, int y, int w, int h);
virtual void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) = 0;
virtual Draw::Texture *MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) = 0;
virtual void DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) = 0;
virtual void Bind2DShader() = 0;

View file

@ -143,14 +143,6 @@ FramebufferManagerD3D11::~FramebufferManagerD3D11() {
quadBuffer_->Release();
fsQuadBuffer_->Release();
if (drawPixelsTex_)
drawPixelsTex_->Release();
if (drawPixelsTexView_)
drawPixelsTexView_->Release();
// Temp FBOs cleared by FramebufferCommon.
delete[] convBuf;
// Stencil cleanup
for (int i = 0; i < 256; i++) {
if (stencilMaskStates_[i])
@ -186,69 +178,52 @@ void FramebufferManagerD3D11::SetDrawEngine(DrawEngineD3D11 *td) {
drawEngine_ = td;
}
void FramebufferManagerD3D11::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) {
// TODO: Check / use D3DCAPS2_DYNAMICTEXTURES?
if (drawPixelsTex_ && (drawPixelsTexW_ != width || drawPixelsTexH_ != height)) {
drawPixelsTex_->Release();
drawPixelsTex_ = nullptr;
drawPixelsTexView_->Release();
drawPixelsTexView_ = nullptr;
}
Draw::Texture *FramebufferManagerD3D11::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) {
auto generateTexture = [&](uint8_t *data, const uint8_t *initData, uint32_t w, uint32_t h, uint32_t d, uint32_t byteStride, uint32_t sliceByteStride) {
for (int y = 0; y < height; y++) {
const u16_le *src16 = (const u16_le *)srcPixels + srcStride * y;
const u32_le *src32 = (const u32_le *)srcPixels + srcStride * y;
u32 *dst = (u32 *)(data + byteStride * y);
switch (srcPixelFormat) {
case GE_FORMAT_565:
ConvertRGB565ToBGRA8888(dst, src16, width);
break;
if (!drawPixelsTex_) {
int usage = 0;
D3D11_TEXTURE2D_DESC desc{};
desc.Usage = D3D11_USAGE_DYNAMIC;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
desc.Width = width;
desc.Height = height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.SampleDesc.Count = 1;
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
ASSERT_SUCCESS(device_->CreateTexture2D(&desc, nullptr, &drawPixelsTex_));
ASSERT_SUCCESS(device_->CreateShaderResourceView(drawPixelsTex_, nullptr, &drawPixelsTexView_));
drawPixelsTexW_ = width;
drawPixelsTexH_ = height;
}
case GE_FORMAT_5551:
ConvertRGBA5551ToBGRA8888(dst, src16, width);
break;
if (!drawPixelsTex_) {
return;
}
case GE_FORMAT_4444:
ConvertRGBA4444ToBGRA8888(dst, src16, width);
break;
D3D11_MAPPED_SUBRESOURCE map;
context_->Map(drawPixelsTex_, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
case GE_FORMAT_8888:
ConvertRGBA8888ToBGRA8888(dst, src32, width);
break;
for (int y = 0; y < height; y++) {
const u16_le *src16 = (const u16_le *)srcPixels + srcStride * y;
const u32_le *src32 = (const u32_le *)srcPixels + srcStride * y;
u32 *dst = (u32 *)((u8 *)map.pData + map.RowPitch * y);
switch (srcPixelFormat) {
case GE_FORMAT_565:
ConvertRGB565ToBGRA8888(dst, src16, width);
break;
case GE_FORMAT_5551:
ConvertRGBA5551ToBGRA8888(dst, src16, width);
break;
case GE_FORMAT_4444:
ConvertRGBA4444ToBGRA8888(dst, src16, width);
break;
case GE_FORMAT_8888:
ConvertRGBA8888ToBGRA8888(dst, src32, width);
break;
case GE_FORMAT_INVALID:
_dbg_assert_msg_(G3D, false, "Invalid pixelFormat passed to DrawPixels().");
break;
case GE_FORMAT_INVALID:
_dbg_assert_msg_(G3D, false, "Invalid pixelFormat passed to DrawPixels().");
break;
}
}
}
};
context_->Unmap(drawPixelsTex_, 0);
context_->PSSetShaderResources(0, 1, &drawPixelsTexView_);
Draw::TextureDesc desc{
Draw::TextureType::LINEAR2D,
Draw::DataFormat::B8G8R8A8_UNORM,
width,
height,
1,
1,
false,
"DrawPixels",
{ (uint8_t *)srcPixels },
generateTexture,
};
Draw::Texture *tex = draw_->CreateTexture(desc);
if (!tex)
ERROR_LOG(G3D, "Failed to create drawpixels texture");
return tex;
}
void FramebufferManagerD3D11::DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) {

View file

@ -68,7 +68,7 @@ protected:
private:
void Bind2DShader() override;
void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override;
Draw::Texture *MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override;
void PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h);
void SimpleBlit(
Draw::Framebuffer *dest, float destX1, float destY1, float destX2, float destY2,
@ -79,12 +79,6 @@ private:
ID3D11DeviceContext *context_;
D3D_FEATURE_LEVEL featureLevel_;
// Used by DrawPixels
ID3D11Texture2D *drawPixelsTex_ = nullptr;
ID3D11ShaderResourceView *drawPixelsTexView_ = nullptr;
int drawPixelsTexW_ = 0;
int drawPixelsTexH_ = 0;
ID3D11VertexShader *quadVertexShader_;
ID3D11PixelShader *quadPixelShader_;
ID3D11InputLayout *quadInputLayout_;
@ -95,8 +89,6 @@ private:
const UINT quadOffset_ = 0;
static const D3D11_INPUT_ELEMENT_DESC g_QuadVertexElements[2];
u8 *convBuf = nullptr;
ID3D11PixelShader *stencilUploadPS_ = nullptr;
ID3D11VertexShader *stencilUploadVS_ = nullptr;
ID3D11InputLayout *stencilUploadInputLayout_ = nullptr;

View file

@ -160,7 +160,9 @@ bool FramebufferManagerD3D11::NotifyStencilUpload(u32 addr, int size, bool skipZ
u16 h = dstBuffer->renderHeight;
float u1 = 1.0f;
float v1 = 1.0f;
MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->bufferWidth, dstBuffer->bufferHeight, u1, v1);
Draw::Texture *tex = MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->bufferWidth, dstBuffer->bufferHeight, u1, v1);
if (!tex)
return false;
if (dstBuffer->fbo) {
draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR });
} else {
@ -189,7 +191,7 @@ bool FramebufferManagerD3D11::NotifyStencilUpload(u32 addr, int size, bool skipZ
context_->IASetInputLayout(stencilUploadInputLayout_);
context_->PSSetShader(stencilUploadPS_, nullptr, 0);
context_->VSSetShader(stencilUploadVS_, nullptr, 0);
context_->PSSetShaderResources(0, 1, &drawPixelsTexView_);
draw_->BindTextures(0, 1, &tex);
context_->RSSetState(stockD3D11.rasterStateNoCull);
context_->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
context_->IASetVertexBuffers(0, 1, &quadBuffer_, &quadStride_, &quadOffset_);
@ -237,6 +239,8 @@ bool FramebufferManagerD3D11::NotifyStencilUpload(u32 addr, int size, bool skipZ
context_->PSSetConstantBuffers(0, 1, &stencilValueBuffer_);
context_->Draw(4, 0);
}
tex->Release();
RebindFramebuffer();
return true;
}

View file

@ -131,13 +131,9 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = {
pFramebufferPixelShader = nullptr;
}
pFramebufferVertexDecl->Release();
if (drawPixelsTex_) {
drawPixelsTex_->Release();
}
for (auto &it : offscreenSurfaces_) {
it.second.surface->Release();
}
delete [] convBuf;
if (stencilUploadPS_) {
stencilUploadPS_->Release();
}
@ -163,87 +159,52 @@ static const D3DVERTEXELEMENT9 g_FramebufferVertexElements[] = {
drawEngine_ = td;
}
void FramebufferManagerDX9::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) {
u8 *convBuf = NULL;
D3DLOCKED_RECT rect;
// TODO: Check / use D3DCAPS2_DYNAMICTEXTURES?
if (drawPixelsTex_ && (drawPixelsTexW_ != width || drawPixelsTexH_ != height)) {
drawPixelsTex_->Release();
drawPixelsTex_ = nullptr;
}
if (!drawPixelsTex_) {
int usage = 0;
D3DPOOL pool = D3DPOOL_MANAGED;
if (deviceEx_) {
pool = D3DPOOL_DEFAULT;
usage = D3DUSAGE_DYNAMIC;
}
HRESULT hr = device_->CreateTexture(width, height, 1, usage, D3DFMT_A8R8G8B8, pool, &drawPixelsTex_, NULL);
if (FAILED(hr)) {
drawPixelsTex_ = nullptr;
ERROR_LOG(G3D, "Failed to create drawpixels texture");
}
drawPixelsTexW_ = width;
drawPixelsTexH_ = height;
}
if (!drawPixelsTex_) {
return;
}
drawPixelsTex_->LockRect(0, &rect, NULL, D3DLOCK_DISCARD);
convBuf = (u8*)rect.pBits;
// Final format is BGRA(directx)
if (srcPixelFormat != GE_FORMAT_8888 || srcStride != 512) {
Draw::Texture *FramebufferManagerDX9::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) {
auto generateTexture = [&](uint8_t *data, const uint8_t *initData, uint32_t w, uint32_t h, uint32_t d, uint32_t byteStride, uint32_t sliceByteStride) {
for (int y = 0; y < height; y++) {
const u16_le *src16 = (const u16_le *)srcPixels + srcStride * y;
const u32_le *src32 = (const u32_le *)srcPixels + srcStride * y;
u32 *dst = (u32 *)(data + byteStride * y);
switch (srcPixelFormat) {
case GE_FORMAT_565:
{
const u16_le *src = (const u16_le *)srcPixels + srcStride * y;
u32 *dst = (u32 *)(convBuf + rect.Pitch * y);
ConvertRGB565ToBGRA8888(dst, src, width);
}
ConvertRGB565ToBGRA8888(dst, src16, width);
break;
// faster
case GE_FORMAT_5551:
{
const u16_le *src = (const u16_le *)srcPixels + srcStride * y;
u32 *dst = (u32 *)(convBuf + rect.Pitch * y);
ConvertRGBA5551ToBGRA8888(dst, src, width);
}
ConvertRGBA5551ToBGRA8888(dst, src16, width);
break;
case GE_FORMAT_4444:
{
const u16_le *src = (const u16_le *)srcPixels + srcStride * y;
u8 *dst = (u8 *)(convBuf + rect.Pitch * y);
ConvertRGBA4444ToBGRA8888((u32 *)dst, src, width);
}
ConvertRGBA4444ToBGRA8888(dst, src16, width);
break;
case GE_FORMAT_8888:
{
const u32_le *src = (const u32_le *)srcPixels + srcStride * y;
u32 *dst = (u32 *)(convBuf + rect.Pitch * y);
ConvertRGBA8888ToBGRA8888(dst, src, width);
}
ConvertRGBA8888ToBGRA8888(dst, src32, width);
break;
case GE_FORMAT_INVALID:
_dbg_assert_msg_(G3D, false, "Invalid pixelFormat passed to DrawPixels().");
break;
}
}
} else {
for (int y = 0; y < height; y++) {
const u32_le *src = (const u32_le *)srcPixels + srcStride * y;
u32 *dst = (u32 *)(convBuf + rect.Pitch * y);
ConvertRGBA8888ToBGRA8888(dst, src, width);
}
}
};
drawPixelsTex_->UnlockRect(0);
device_->SetTexture(0, drawPixelsTex_);
// D3DXSaveTextureToFile("game:\\cc.png", D3DXIFF_PNG, drawPixelsTex_, NULL);
Draw::TextureDesc desc{
Draw::TextureType::LINEAR2D,
Draw::DataFormat::B8G8R8A8_UNORM,
width,
height,
1,
1,
false,
"DrawPixels",
{ (uint8_t *)srcPixels },
generateTexture,
};
Draw::Texture *tex = draw_->CreateTexture(desc);
if (!tex)
ERROR_LOG(G3D, "Failed to create drawpixels texture");
return tex;
}
void FramebufferManagerDX9::DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) {

View file

@ -27,7 +27,6 @@
#include "GPU/GPUCommon.h"
#include "GPU/Common/FramebufferCommon.h"
#include "ext/native/thin3d/thin3d.h"
namespace DX9 {
@ -77,7 +76,7 @@ protected:
void UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb) override;
private:
void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override;
Draw::Texture *MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override;
void PackFramebufferSync_(VirtualFramebuffer *vfb, int x, int y, int w, int h) override;
void PackDepthbuffer(VirtualFramebuffer *vfb, int x, int y, int w, int h);
bool GetRenderTargetFramebuffer(LPDIRECT3DSURFACE9 renderTarget, LPDIRECT3DSURFACE9 offscreen, int w, int h, GPUDebugBuffer &buffer);
@ -85,17 +84,10 @@ private:
LPDIRECT3DDEVICE9 device_;
LPDIRECT3DDEVICE9 deviceEx_;
// Used by DrawPixels
LPDIRECT3DTEXTURE9 drawPixelsTex_ = nullptr;
int drawPixelsTexW_;
int drawPixelsTexH_;
LPDIRECT3DVERTEXSHADER9 pFramebufferVertexShader = nullptr;
LPDIRECT3DPIXELSHADER9 pFramebufferPixelShader = nullptr;
LPDIRECT3DVERTEXDECLARATION9 pFramebufferVertexDecl = nullptr;
u8 *convBuf = nullptr;
LPDIRECT3DPIXELSHADER9 stencilUploadPS_ = nullptr;
LPDIRECT3DVERTEXSHADER9 stencilUploadVS_ = nullptr;
bool stencilUploadFailed_ = false;

View file

@ -201,7 +201,7 @@ bool FramebufferManagerDX9::NotifyStencilUpload(u32 addr, int size, bool skipZer
float u1 = 1.0f;
float v1 = 1.0f;
MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->bufferWidth, dstBuffer->bufferHeight, u1, v1);
Draw::Texture *tex = MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->bufferWidth, dstBuffer->bufferHeight, u1, v1);
device_->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_STENCIL, D3DCOLOR_RGBA(0, 0, 0, 0), 0.0f, 0);
@ -220,7 +220,7 @@ bool FramebufferManagerDX9::NotifyStencilUpload(u32 addr, int size, bool skipZer
device_->SetPixelShader(stencilUploadPS_);
device_->SetVertexShader(stencilUploadVS_);
device_->SetTexture(0, drawPixelsTex_);
draw_->BindTextures(0, 1, &tex);
shaderManagerDX9_->DirtyLastShader();
textureCacheDX9_->ForgetLastTexture();
@ -248,6 +248,8 @@ bool FramebufferManagerDX9::NotifyStencilUpload(u32 addr, int size, bool skipZer
ERROR_LOG_REPORT(G3D, "Failed to draw stencil bit %x: %08x", i, hr);
}
}
tex->Release();
dxstate.stencilMask.set(0xFF);
dxstate.viewport.restore();
RebindFramebuffer();

View file

@ -150,10 +150,6 @@ void FramebufferManagerGLES::DestroyDeviceObjects() {
render_->DeleteProgram(draw2dprogram_);
draw2dprogram_ = nullptr;
}
if (drawPixelsTex_) {
render_->DeleteTexture(drawPixelsTex_);
drawPixelsTex_ = 0;
}
if (stencilUploadProgram_) {
render_->DeleteProgram(stencilUploadProgram_);
stencilUploadProgram_ = nullptr;
@ -170,52 +166,54 @@ FramebufferManagerGLES::~FramebufferManagerGLES() {
delete [] convBuf_;
}
void FramebufferManagerGLES::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) {
if (drawPixelsTex_) {
render_->DeleteTexture(drawPixelsTex_);
}
drawPixelsTex_ = render_->CreateTexture(GL_TEXTURE_2D);
drawPixelsTexW_ = width;
drawPixelsTexH_ = height;
drawPixelsTexFormat_ = srcPixelFormat;
Draw::Texture *FramebufferManagerGLES::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) {
// TODO: We can just change the texture format and flip some bits around instead of this.
// Could share code with the texture cache perhaps.
u32 neededSize = width * height * 4;
u8 *convBuf = new u8[neededSize];
for (int y = 0; y < height; y++) {
const u16_le *src16 = (const u16_le *)srcPixels + srcStride * y;
const u32_le *src32 = (const u32_le *)srcPixels + srcStride * y;
u32 *dst = (u32 *)convBuf + width * y;
switch (srcPixelFormat) {
case GE_FORMAT_565:
ConvertRGBA565ToRGBA8888((u32 *)dst, src16, width);
break;
auto generateTexture = [&](uint8_t *data, const uint8_t *initData, uint32_t w, uint32_t h, uint32_t d, uint32_t byteStride, uint32_t sliceByteStride) {
for (int y = 0; y < height; y++) {
const u16_le *src16 = (const u16_le *)srcPixels + srcStride * y;
const u32_le *src32 = (const u32_le *)srcPixels + srcStride * y;
u32 *dst = (u32 *)(data + byteStride * y);
switch (srcPixelFormat) {
case GE_FORMAT_565:
ConvertRGBA565ToRGBA8888(dst, src16, width);
break;
case GE_FORMAT_5551:
ConvertRGBA5551ToRGBA8888((u32 *)dst, src16, width);
break;
case GE_FORMAT_5551:
ConvertRGBA5551ToRGBA8888(dst, src16, width);
break;
case GE_FORMAT_4444:
ConvertRGBA4444ToRGBA8888((u32 *)dst, src16, width);
break;
case GE_FORMAT_4444:
ConvertRGBA4444ToRGBA8888(dst, src16, width);
break;
case GE_FORMAT_8888:
memcpy(dst, src32, 4 * width);
break;
case GE_FORMAT_8888:
memcpy(dst, src32, 4 * width);
break;
case GE_FORMAT_INVALID:
_dbg_assert_msg_(G3D, false, "Invalid pixelFormat passed to DrawPixels().");
break;
case GE_FORMAT_INVALID:
_dbg_assert_msg_(G3D, false, "Invalid pixelFormat passed to DrawPixels().");
break;
}
}
}
render_->TextureImage(drawPixelsTex_, 0, width, height, Draw::DataFormat::R8G8B8A8_UNORM, convBuf, GLRAllocType::NEW, false);
render_->FinalizeTexture(drawPixelsTex_, 0, false);
};
// TODO: Return instead?
render_->BindTexture(TEX_SLOT_PSP_TEXTURE, drawPixelsTex_);
Draw::TextureDesc desc{
Draw::TextureType::LINEAR2D,
Draw::DataFormat::R8G8B8A8_UNORM,
width,
height,
1,
1,
false,
"DrawPixels",
{ (uint8_t *)srcPixels },
generateTexture,
};
Draw::Texture *tex = draw_->CreateTexture(desc);
if (!tex)
ERROR_LOG(G3D, "Failed to create drawpixels texture");
return tex;
}
// x, y, w, h are relative coordinates against destW/destH, which is not very intuitive.

View file

@ -73,7 +73,7 @@ private:
void CreateDeviceObjects();
void DestroyDeviceObjects();
void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override;
Draw::Texture *MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override;
void Bind2DShader() override;
void CompileDraw2DProgram();
@ -81,14 +81,9 @@ private:
GLRenderManager *render_;
// Used by DrawPixels
GLRTexture *drawPixelsTex_ = nullptr;
GEBufferFormat drawPixelsTexFormat_ = GE_FORMAT_INVALID;
int drawPixelsTexW_ = 0;
int drawPixelsTexH_ = 0;
u8 *convBuf_ = nullptr;
u32 convBufSize_ = 0;
GLRProgram *draw2dprogram_ = nullptr;
GLRProgram *stencilUploadProgram_ = nullptr;

View file

@ -177,8 +177,9 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe
float u1 = 1.0f;
float v1 = 1.0f;
MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->width, dstBuffer->height, u1, v1);
Draw::Texture *tex = MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->width, dstBuffer->height, u1, v1);
textureCacheGL_->ForgetLastTexture();
draw_->BindTextures(TEX_SLOT_PSP_TEXTURE, 1, &tex);
// We must bind the program after starting the render pass, and set the color mask after clearing.
render_->SetScissor({ 0, 0, w, h });
@ -211,6 +212,7 @@ bool FramebufferManagerGLES::NotifyStencilUpload(u32 addr, int size, bool skipZe
draw_->BlitFramebuffer(blitFBO, 0, 0, w, h, dstBuffer->fbo, 0, 0, dstBuffer->renderWidth, dstBuffer->renderHeight, Draw::FB_STENCIL_BIT, Draw::FB_BLIT_NEAREST);
}
tex->Release();
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_VIEWPORTSCISSOR_STATE);
RebindFramebuffer();
return true;

View file

@ -270,7 +270,6 @@ void SoftGPU::CopyToCurrentFboFromDisplayRam(int srcwidth, int srcheight) {
outputFlags |= OutputFlags::BACKBUFFER_FLIPPED;
}
// TODO, also deal with RB swizzle.
PostShaderUniforms uniforms{};
presentation_->CalculatePostShaderUniforms(desc.width, desc.height, false, &uniforms);

View file

@ -85,8 +85,6 @@ FramebufferManagerVulkan::FramebufferManagerVulkan(Draw::DrawContext *draw, Vulk
}
FramebufferManagerVulkan::~FramebufferManagerVulkan() {
delete[] convBuf_;
DeviceLost();
}
@ -127,9 +125,6 @@ void FramebufferManagerVulkan::InitDeviceObjects() {
}
void FramebufferManagerVulkan::DestroyDeviceObjects() {
delete drawPixelsTex_;
drawPixelsTex_ = nullptr;
if (fsBasicTex_ != VK_NULL_HANDLE) {
vulkan2D_->PurgeFragmentShader(fsBasicTex_);
vulkan_->Delete().QueueDeleteShaderModule(fsBasicTex_);
@ -181,57 +176,23 @@ void FramebufferManagerVulkan::Init() {
Resized();
}
void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) {
if (drawPixelsTex_) {
delete drawPixelsTex_;
drawPixelsTex_ = nullptr;
}
VkCommandBuffer initCmd = (VkCommandBuffer)draw_->GetNativeObject(Draw::NativeObject::INIT_COMMANDBUFFER);
// There's only ever a few of these alive, don't need to stress the allocator with these big ones.
// OR NOT! Hot Shot Golf (#12355) does tons of these in a frame in some situations! So actually,
// we do use an allocator. In fact, I've now banned allocator-less textures.
drawPixelsTex_ = new VulkanTexture(vulkan_);
drawPixelsTex_->SetTag("DrawPixels");
if (!drawPixelsTex_->CreateDirect(initCmd, allocator_, width, height, 1, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT)) {
// out of memory?
delete drawPixelsTex_;
drawPixelsTex_ = nullptr;
overrideImageView_ = VK_NULL_HANDLE;
return;
}
// Initialize backbuffer texture for DrawPixels
drawPixelsTexFormat_ = srcPixelFormat;
// TODO: We can just change the texture format and flip some bits around instead of this.
// Could share code with the texture cache perhaps.
// TODO: Could also convert directly into the pushbuffer easily.
const uint8_t *data = srcPixels;
if (srcPixelFormat != GE_FORMAT_8888 || srcStride != width) {
u32 neededSize = width * height * 4;
if (!convBuf_ || convBufSize_ < neededSize) {
delete[] convBuf_;
convBuf_ = new u8[neededSize];
convBufSize_ = neededSize;
}
data = convBuf_;
Draw::Texture *FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) {
auto generateTexture = [&](uint8_t *data, const uint8_t *initData, uint32_t w, uint32_t h, uint32_t d, uint32_t byteStride, uint32_t sliceByteStride) {
for (int y = 0; y < height; y++) {
const u16_le *src16 = (const u16_le *)srcPixels + srcStride * y;
const u32_le *src32 = (const u32_le *)srcPixels + srcStride * y;
u32 *dst = (u32 *)convBuf_ + width * y;
u32 *dst = (u32 *)(data + byteStride * y);
switch (srcPixelFormat) {
case GE_FORMAT_565:
ConvertRGBA565ToRGBA8888((u32 *)dst, src16, width);
ConvertRGBA565ToRGBA8888(dst, src16, width);
break;
case GE_FORMAT_5551:
ConvertRGBA5551ToRGBA8888((u32 *)dst, src16, width);
ConvertRGBA5551ToRGBA8888(dst, src16, width);
break;
case GE_FORMAT_4444:
ConvertRGBA4444ToRGBA8888((u32 *)dst, src16, width);
ConvertRGBA4444ToRGBA8888(dst, src16, width);
break;
case GE_FORMAT_8888:
@ -243,14 +204,28 @@ void FramebufferManagerVulkan::MakePixelTexture(const u8 *srcPixels, GEBufferFor
break;
}
}
}
};
VkBuffer buffer;
size_t offset = push_->Push(data, width * height * 4, &buffer);
drawPixelsTex_->UploadMip(initCmd, 0, width, height, buffer, (uint32_t)offset, width);
drawPixelsTex_->EndCreate(initCmd);
// Hot Shot Golf (#12355) does tons of these in a frame in some situations! So actually,
// we do use an allocator. In fact, I've now banned allocator-less textures.
overrideImageView_ = drawPixelsTex_->GetImageView();
Draw::TextureDesc desc{
Draw::TextureType::LINEAR2D,
Draw::DataFormat::R8G8B8A8_UNORM,
width,
height,
1,
1,
false,
"DrawPixels",
{ (uint8_t *)srcPixels },
generateTexture,
};
// TODO: Use allocator_ somehow?
Draw::Texture *tex = draw_->CreateTexture(desc);
if (!tex)
ERROR_LOG(G3D, "Failed to create drawpixels texture");
return tex;
}
void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, float h, float destW, float destH, float u0, float v0, float u1, float v1, int uvRotation, int flags) {
@ -305,9 +280,7 @@ void FramebufferManagerVulkan::DrawActiveTexture(float x, float y, float w, floa
VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
VkImageView view = overrideImageView_ ? overrideImageView_ : (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE0_IMAGEVIEW);
if ((flags & DRAWTEX_KEEP_TEX) == 0)
overrideImageView_ = VK_NULL_HANDLE;
VkImageView view = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE0_IMAGEVIEW);
VkDescriptorSet descSet = vulkan2D_->GetDescriptorSet(view, (flags & DRAWTEX_LINEAR) ? linearSampler_ : nearestSampler_, VK_NULL_HANDLE, VK_NULL_HANDLE);
VkBuffer vbuffer;
VkDeviceSize offset = push_->Push(vtx, sizeof(vtx), &vbuffer);

View file

@ -79,7 +79,7 @@ protected:
private:
// The returned texture does not need to be free'd, might be returned from a pool (currently single entry)
void MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override;
Draw::Texture *MakePixelTexture(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height, float &u1, float &v1) override;
void InitDeviceObjects();
void DestroyDeviceObjects();
@ -88,12 +88,6 @@ private:
// Used to keep track of command buffers here but have moved all that into Thin3D.
// Used by DrawPixels
VulkanTexture *drawPixelsTex_ = nullptr;
GEBufferFormat drawPixelsTexFormat_ = GE_FORMAT_INVALID;
u8 *convBuf_ = nullptr;
u32 convBufSize_ = 0;
TextureCacheVulkan *textureCacheVulkan_ = nullptr;
ShaderManagerVulkan *shaderManagerVulkan_ = nullptr;
DrawEngineVulkan *drawEngineVulkan_ = nullptr;
@ -118,9 +112,6 @@ private:
VkSampler linearSampler_;
VkSampler nearestSampler_;
// hack!
VkImageView overrideImageView_ = VK_NULL_HANDLE;
// Simple 2D drawing engine.
Vulkan2D *vulkan2D_;
};

View file

@ -163,7 +163,7 @@ bool FramebufferManagerVulkan::NotifyStencilUpload(u32 addr, int size, bool skip
u16 h = dstBuffer->renderHeight;
float u1 = 1.0f;
float v1 = 1.0f;
MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->bufferWidth, dstBuffer->bufferHeight, u1, v1);
Draw::Texture *tex = MakePixelTexture(src, dstBuffer->format, dstBuffer->fb_stride, dstBuffer->bufferWidth, dstBuffer->bufferHeight, u1, v1);
if (dstBuffer->fbo) {
draw_->BindFramebufferAsRenderTarget(dstBuffer->fbo, { Draw::RPAction::KEEP, Draw::RPAction::KEEP, Draw::RPAction::CLEAR });
} else {
@ -176,7 +176,9 @@ bool FramebufferManagerVulkan::NotifyStencilUpload(u32 addr, int size, bool skip
renderManager->SetScissor({ { 0, 0, },{ (uint32_t)w, (uint32_t)h } });
gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE);
VkDescriptorSet descSet = vulkan2D_->GetDescriptorSet(overrideImageView_, nearestSampler_, VK_NULL_HANDLE, VK_NULL_HANDLE);
draw_->BindTextures(0, 1, &tex);
VkImageView drawPixelsImageView = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE0_IMAGEVIEW);
VkDescriptorSet descSet = vulkan2D_->GetDescriptorSet(drawPixelsImageView, nearestSampler_, VK_NULL_HANDLE, VK_NULL_HANDLE);
// Note: Even with skipZero, we don't necessarily start framebuffers at 0 in Vulkan. Clear anyway.
// Not an actual clear, because we need to draw to alpha only as well.
@ -212,7 +214,7 @@ bool FramebufferManagerVulkan::NotifyStencilUpload(u32 addr, int size, bool skip
renderManager->Draw(vulkan2D_->GetPipelineLayout(), descSet, 0, nullptr, VK_NULL_HANDLE, 0, 3); // full screen triangle
}
overrideImageView_ = VK_NULL_HANDLE;
tex->Release();
RebindFramebuffer();
return true;
}

View file

@ -8,8 +8,9 @@
#include <cstdint>
#include <cstddef>
#include <vector>
#include <functional>
#include <string>
#include <vector>
#include "base/logging.h"
#include "DataFormat.h"
@ -519,6 +520,10 @@ struct DeviceCaps {
std::string deviceName; // The device name to use when creating the thin3d context, to get the same one.
};
// Use to write data directly to memory. initData is the pointer passed in TextureDesc.
// Important: only write to the provided pointer, don't read from it.
typedef std::function<void(uint8_t *data, const uint8_t *initData, uint32_t w, uint32_t h, uint32_t d, uint32_t byteStride, uint32_t sliceByteStride)> TextureCallback;
struct TextureDesc {
TextureType type;
DataFormat format;
@ -530,7 +535,8 @@ struct TextureDesc {
// Optional, for tracking memory usage.
std::string tag;
// Does not take ownership over pointed-to data.
std::vector<uint8_t *> initData;
std::vector<const uint8_t *> initData;
TextureCallback initDataCallback;
};
enum class RPAction {

View file

@ -689,16 +689,18 @@ public:
~D3D11Texture() {
if (tex)
tex->Release();
if (stagingTex)
stagingTex->Release();
if (view)
view->Release();
}
ID3D11Texture2D *tex = nullptr;
ID3D11Texture2D *stagingTex = nullptr;
ID3D11ShaderResourceView *view = nullptr;
};
Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) {
if (!(GetDataFormatSupport(desc.format) & FMT_TEXTURE)) {
// D3D11 does not support this format as a texture format.
return nullptr;
@ -720,25 +722,45 @@ Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) {
descColor.Format = dataFormatToD3D11(desc.format);
descColor.SampleDesc.Count = 1;
descColor.SampleDesc.Quality = 0;
if (desc.initDataCallback) {
descColor.Usage = D3D11_USAGE_STAGING;
descColor.BindFlags = 0;
descColor.MiscFlags = 0;
descColor.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
HRESULT hr = device_->CreateTexture2D(&descColor, nullptr, &tex->stagingTex);
if (!SUCCEEDED(hr)) {
delete tex;
return nullptr;
}
}
descColor.Usage = D3D11_USAGE_DEFAULT;
descColor.BindFlags = generateMips ? (D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET) : D3D11_BIND_SHADER_RESOURCE;
descColor.MiscFlags = generateMips ? D3D11_RESOURCE_MISC_GENERATE_MIPS : 0;
descColor.CPUAccessFlags = 0;
D3D11_SUBRESOURCE_DATA *initDataParam = nullptr;
D3D11_SUBRESOURCE_DATA initData[12]{};
if (desc.initData.size() && !generateMips) {
std::vector<uint8_t> initDataBuffer[12];
if (desc.initData.size() && !generateMips && !desc.initDataCallback) {
int w = desc.width;
int h = desc.height;
int d = desc.depth;
for (int i = 0; i < (int)desc.initData.size(); i++) {
uint32_t byteStride = w * (uint32_t)DataFormatSizeInBytes(desc.format);
initData[i].pSysMem = desc.initData[i];
initData[i].SysMemPitch = (UINT)(w * DataFormatSizeInBytes(desc.format));
initData[i].SysMemSlicePitch = (UINT)(w * h * DataFormatSizeInBytes(desc.format));
w /= 2;
h /= 2;
initData[i].SysMemPitch = (UINT)byteStride;
initData[i].SysMemSlicePitch = (UINT)(h * byteStride);
w = (w + 1) / 2;
h = (h + 1) / 2;
d = (d + 1) / 2;
}
initDataParam = initData;
}
HRESULT hr = device_->CreateTexture2D(&descColor, (desc.initData.size() && !generateMips) ? initData : nullptr, &tex->tex);
HRESULT hr = device_->CreateTexture2D(&descColor, initDataParam, &tex->tex);
if (!SUCCEEDED(hr)) {
delete tex;
return nullptr;
@ -749,8 +771,52 @@ Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) {
return nullptr;
}
if (generateMips && desc.initData.size() >= 1) {
context_->UpdateSubresource(tex->tex, 0, nullptr, desc.initData[0], desc.width * (int)DataFormatSizeInBytes(desc.format), 0);
if (desc.initDataCallback) {
D3D11_MAPPED_SUBRESOURCE mapped;
hr = context_->Map(tex->stagingTex, 0, D3D11_MAP_WRITE, 0, &mapped);
if (!SUCCEEDED(hr)) {
delete tex;
return nullptr;
}
desc.initDataCallback((uint8_t *)mapped.pData, desc.initData[0], desc.width, desc.height, desc.depth, mapped.RowPitch, mapped.DepthPitch);
context_->Unmap(tex->tex, 0);
context_->CopyResource(tex->stagingTex, tex->stagingTex);
tex->stagingTex->Release();
tex->stagingTex = nullptr;
} else {
uint32_t byteStride = desc.width * (uint32_t)DataFormatSizeInBytes(desc.format);
context_->UpdateSubresource(tex->tex, 0, nullptr, desc.initData[0], byteStride, 0);
}
context_->GenerateMips(tex->view);
} else if (desc.initDataCallback) {
int w = desc.width;
int h = desc.height;
int d = desc.depth;
for (int i = 0; i < (int)desc.initData.size(); i++) {
D3D11_MAPPED_SUBRESOURCE mapped;
hr = context_->Map(tex->stagingTex, i, D3D11_MAP_WRITE, 0, &mapped);
if (!SUCCEEDED(hr)) {
if (i == 0) {
delete tex;
return nullptr;
} else {
break;
}
}
desc.initDataCallback((uint8_t *)mapped.pData, desc.initData[i], w, h, d, mapped.RowPitch, mapped.DepthPitch);
context_->Unmap(tex->stagingTex, i);
w = (w + 1) / 2;
h = (h + 1) / 2;
d = (d + 1) / 2;
}
context_->CopyResource(tex->tex, tex->stagingTex);
tex->stagingTex->Release();
tex->stagingTex = nullptr;
}
return tex;
}

View file

@ -133,6 +133,7 @@ static const int primCountDivisor[] = {
D3DFORMAT FormatToD3DFMT(DataFormat fmt) {
switch (fmt) {
case DataFormat::R8G8B8A8_UNORM: return D3DFMT_A8R8G8B8;
case DataFormat::B8G8R8A8_UNORM: return D3DFMT_A8R8G8B8;
case DataFormat::R4G4B4A4_UNORM_PACK16: return D3DFMT_A4R4G4B4; // emulated
case DataFormat::B4G4R4A4_UNORM_PACK16: return D3DFMT_A4R4G4B4; // native
case DataFormat::A4R4G4B4_UNORM_PACK16: return D3DFMT_A4R4G4B4; // emulated
@ -312,7 +313,7 @@ public:
void SetToSampler(LPDIRECT3DDEVICE9 device, int sampler);
private:
void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data);
void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback callback);
bool Create(const TextureDesc &desc);
LPDIRECT3DDEVICE9 device_;
LPDIRECT3DDEVICE9EX deviceEx_;
@ -384,8 +385,14 @@ bool D3D9Texture::Create(const TextureDesc &desc) {
// 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, width_, height_, depth_, i, 0, desc.initData[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;
}
}
return true;
@ -396,11 +403,10 @@ inline uint32_t Shuffle8888(uint32_t x) {
return (x & 0xFF00FF00) | ((x >> 16) & 0xFF) | ((x << 16) & 0xFF0000);
}
void D3D9Texture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) {
void D3D9Texture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback callback) {
if (!tex_)
return;
if (level == 0) {
width_ = width;
height_ = height;
@ -417,6 +423,14 @@ void D3D9Texture::SetImageData(int x, int y, int z, int width, int height, int d
D3DLOCKED_RECT rect;
if (x == 0 && y == 0) {
tex_->LockRect(level, &rect, NULL, D3DLOCK_DISCARD);
if (callback) {
callback((uint8_t *)rect.pBits, data, width, height, depth, rect.Pitch, height * rect.Pitch);
// Now this is the source. All conversions below support in-place.
data = (const uint8_t *)rect.pBits;
stride = rect.Pitch;
}
for (int i = 0; i < height; i++) {
uint8_t *dest = (uint8_t *)rect.pBits + rect.Pitch * i;
const uint8_t *source = data + stride * i;
@ -431,7 +445,8 @@ void D3D9Texture::SetImageData(int x, int y, int z, int width, int height, int d
case DataFormat::A4R4G4B4_UNORM_PACK16:
case DataFormat::A1R5G5B5_UNORM_PACK16:
// Native
memcpy(dest, source, width * sizeof(uint16_t));
if (data != rect.pBits)
memcpy(dest, source, width * sizeof(uint16_t));
break;
case DataFormat::R8G8B8A8_UNORM:
@ -441,7 +456,8 @@ void D3D9Texture::SetImageData(int x, int y, int z, int width, int height, int d
break;
case DataFormat::B8G8R8A8_UNORM:
memcpy(dest, source, sizeof(uint32_t) * width);
if (data != rect.pBits)
memcpy(dest, source, sizeof(uint32_t) * width);
break;
default:
// Unhandled data format copy.

View file

@ -655,7 +655,7 @@ public:
}
private:
void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data);
void SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data, TextureCallback callback);
GLRenderManager *render_;
GLRTexture *tex_;
@ -680,14 +680,15 @@ OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) :
canWrap_ = isPowerOf2(width_) && isPowerOf2(height_);
mipLevels_ = desc.mipLevels;
if (!desc.initData.size())
if (desc.initData.empty())
return;
int level = 0;
for (auto data : desc.initData) {
SetImageData(0, 0, 0, width_, height_, depth_, level, 0, data);
SetImageData(0, 0, 0, width_, height_, depth_, level, 0, data, desc.initDataCallback);
width_ = (width_ + 1) / 2;
height_ = (height_ + 1) / 2;
depth_ = (depth_ + 1) / 2;
level++;
}
mipLevels_ = desc.generateMips ? desc.mipLevels : level;
@ -699,7 +700,6 @@ OpenGLTexture::OpenGLTexture(GLRenderManager *render, const TextureDesc &desc) :
generatedMips_ = true;
}
render->FinalizeTexture(tex_, mipLevels_, genMips);
}
OpenGLTexture::~OpenGLTexture() {
@ -732,8 +732,8 @@ void MoveABit(u16 *dest, const u16 *src, size_t count) {
}
}
void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int depth, int level, int stride, const uint8_t *data) {
if (width != width_ || height != height_ || depth != depth_) {
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) {
if ((width != width_ || height != height_ || depth != depth_) && level == 0) {
// When switching to texStorage we need to handle this correctly.
width_ = width;
height_ = height;
@ -745,19 +745,28 @@ void OpenGLTexture::SetImageData(int x, int y, int z, int width, int height, int
size_t alignment = DataFormatSizeInBytes(format_);
// Make a copy of data with stride eliminated.
uint8_t *texData = new uint8_t[(size_t)(width * height * alignment)];
uint8_t *texData = new uint8_t[(size_t)(width * height * depth * alignment)];
// Emulate support for DataFormat::A1R5G5B5_UNORM_PACK16.
if (format_ == DataFormat::A1R5G5B5_UNORM_PACK16) {
format_ = DataFormat::R5G5B5A1_UNORM_PACK16;
for (int y = 0; y < height; y++) {
MoveABit((u16 *)(texData + y * width * alignment), (const u16 *)(data + y * stride * alignment), width);
if (callback) {
callback(texData, data, width, height, depth, width * (int)alignment, height * width * (int)alignment);
if (format_ == DataFormat::A1R5G5B5_UNORM_PACK16) {
format_ = DataFormat::R5G5B5A1_UNORM_PACK16;
MoveABit((u16 *)texData, (const u16 *)texData, width * height * depth);
}
} else {
for (int y = 0; y < height; y++) {
memcpy(texData + y * width * alignment, data + y * stride * alignment, width * alignment);
// Emulate support for DataFormat::A1R5G5B5_UNORM_PACK16.
if (format_ == DataFormat::A1R5G5B5_UNORM_PACK16) {
format_ = DataFormat::R5G5B5A1_UNORM_PACK16;
for (int y = 0; y < height; y++) {
MoveABit((u16 *)(texData + y * width * alignment), (const u16 *)(data + y * stride * alignment), width);
}
} else {
for (int y = 0; y < height; y++) {
memcpy(texData + y * width * alignment, data + y * stride * alignment, width * alignment);
}
}
}
render_->TextureImage(tex_, level, width, height, format_, texData);
}

View file

@ -711,15 +711,22 @@ bool VKTexture::Create(VkCommandBuffer cmd, VulkanPushBuffer *push, const Textur
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 * bytesPerPixel;
offset = push->PushAligned((const void *)desc.initData[i], size, 16, &buf);
size_t size = w * h * d * bytesPerPixel;
if (desc.initDataCallback) {
uint8_t *dest = (uint8_t *)push->PushAligned(size, &offset, &buf, 16);
desc.initDataCallback(dest, desc.initData[0], w, h, d, w * bytesPerPixel, h * w * bytesPerPixel);
} else {
offset = push->PushAligned((const void *)desc.initData[i], size, 16, &buf);
}
vkTex_->UploadMip(cmd, i, w, h, buf, offset, w);
w = (w + 1) / 2;
h = (h + 1) / 2;
d = (d + 1) / 2;
}
// Generate the rest of the mips automatically.
for (; i < mipLevels_; i++) {