From eadc064e2c5f18a6d1cfd1b5bbd436c8945ae722 Mon Sep 17 00:00:00 2001 From: Ivan Epifanov Date: Wed, 23 Mar 2022 19:14:28 +0300 Subject: [PATCH] Vita: add native YUV textures support. * Fail if texture init fails. * Refactor and cleanup. --- src/render/vitagxm/SDL_render_vita_gxm.c | 285 ++++++++++++++++-- .../vitagxm/SDL_render_vita_gxm_tools.c | 57 ++-- .../vitagxm/SDL_render_vita_gxm_tools.h | 5 +- .../vitagxm/SDL_render_vita_gxm_types.h | 9 +- 4 files changed, 298 insertions(+), 58 deletions(-) diff --git a/src/render/vitagxm/SDL_render_vita_gxm.c b/src/render/vitagxm/SDL_render_vita_gxm.c index 8a8877849..4d5530443 100644 --- a/src/render/vitagxm/SDL_render_vita_gxm.c +++ b/src/render/vitagxm/SDL_render_vita_gxm.c @@ -61,6 +61,11 @@ static int VITA_GXM_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * text const Uint8 *Uplane, int Upitch, const Uint8 *Vplane, int Vpitch); +static int VITA_GXM_UpdateTextureNV(SDL_Renderer * renderer, SDL_Texture * texture, + const SDL_Rect * rect, + const Uint8 *Yplane, int Ypitch, + const Uint8 *UVplane, int UVpitch); + static int VITA_GXM_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, void **pixels, int *pitch); @@ -105,12 +110,16 @@ SDL_RenderDriver VITA_GXM_RenderDriver = { .info = { .name = "VITA gxm", .flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE, - .num_texture_formats = 4, + .num_texture_formats = 8, .texture_formats = { [0] = SDL_PIXELFORMAT_ABGR8888, [1] = SDL_PIXELFORMAT_ARGB8888, [2] = SDL_PIXELFORMAT_RGB565, - [3] = SDL_PIXELFORMAT_BGR565 + [3] = SDL_PIXELFORMAT_BGR565, + [4] = SDL_PIXELFORMAT_YV12, + [5] = SDL_PIXELFORMAT_IYUV, + [6] = SDL_PIXELFORMAT_NV12, + [7] = SDL_PIXELFORMAT_NV21, }, .max_texture_width = 4096, .max_texture_height = 4096, @@ -133,6 +142,15 @@ PixelFormatToVITAFMT(Uint32 format) return SCE_GXM_TEXTURE_FORMAT_U5U6U5_RGB; case SDL_PIXELFORMAT_BGR565: return SCE_GXM_TEXTURE_FORMAT_U5U6U5_BGR; + case SDL_PIXELFORMAT_YV12: + return SCE_GXM_TEXTURE_FORMAT_YVU420P3_CSC0; + case SDL_PIXELFORMAT_IYUV: + return SCE_GXM_TEXTURE_FORMAT_YUV420P3_CSC0; + // should be the other way around. looks like SCE bug. + case SDL_PIXELFORMAT_NV12: + return SCE_GXM_TEXTURE_FORMAT_YVU420P2_CSC0; + case SDL_PIXELFORMAT_NV21: + return SCE_GXM_TEXTURE_FORMAT_YUV420P2_CSC0; default: return SCE_GXM_TEXTURE_FORMAT_U8U8U8U8_ABGR; } @@ -228,6 +246,7 @@ VITA_GXM_CreateRenderer(SDL_Window *window, Uint32 flags) renderer->UpdateTexture = VITA_GXM_UpdateTexture; #if SDL_HAVE_YUV renderer->UpdateTextureYUV = VITA_GXM_UpdateTextureYUV; + renderer->UpdateTextureNV = VITA_GXM_UpdateTextureNV; #endif renderer->LockTexture = VITA_GXM_LockTexture; renderer->UnlockTexture = VITA_GXM_UnlockTexture; @@ -295,7 +314,17 @@ VITA_GXM_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture) return SDL_OutOfMemory(); } - vita_texture->tex = create_gxm_texture(data, texture->w, texture->h, PixelFormatToVITAFMT(texture->format), (texture->access == SDL_TEXTUREACCESS_TARGET)); + vita_texture->tex = create_gxm_texture( + data, + texture->w, + texture->h, + PixelFormatToVITAFMT(texture->format), + (texture->access == SDL_TEXTUREACCESS_TARGET), + &(vita_texture->w), + &(vita_texture->h), + &(vita_texture->pitch), + &(vita_texture->wscale) + ); if (!vita_texture->tex) { SDL_free(vita_texture); @@ -306,38 +335,129 @@ VITA_GXM_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture) VITA_GXM_SetTextureScaleMode(renderer, texture, texture->scaleMode); - vita_texture->w = gxm_texture_get_width(vita_texture->tex); - vita_texture->h = gxm_texture_get_height(vita_texture->tex); - vita_texture->pitch = gxm_texture_get_stride(vita_texture->tex); +#if SDL_HAVE_YUV + vita_texture->yuv = ((texture->format == SDL_PIXELFORMAT_IYUV) || (texture->format == SDL_PIXELFORMAT_YV12)); + vita_texture->nv12 = ((texture->format == SDL_PIXELFORMAT_NV12) || (texture->format == SDL_PIXELFORMAT_NV21)); +#endif return 0; } +static void VITA_GXM_SetYUVProfile(SDL_Renderer * renderer, SDL_Texture *texture) +{ + VITA_GXM_RenderData *data = (VITA_GXM_RenderData *) renderer->driverdata; + int ret = 0; + switch (SDL_GetYUVConversionModeForResolution(texture->w, texture->h)) { + case SDL_YUV_CONVERSION_BT601: + ret = sceGxmSetYuvProfile(data->gxm_context, 0, SCE_GXM_YUV_PROFILE_BT601_STANDARD); + break; + case SDL_YUV_CONVERSION_BT709: + ret = sceGxmSetYuvProfile(data->gxm_context, 0, SCE_GXM_YUV_PROFILE_BT709_STANDARD); + break; + case SDL_YUV_CONVERSION_JPEG: + default: + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Unsupported YUV profile: %d\n", SDL_GetYUVConversionModeForResolution(texture->w, texture->h)); + break; + } + + if (ret < 0) { + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "Setting YUV profile failed: %x\n", ret); + } +} static int VITA_GXM_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch) { - const Uint8 *src; + VITA_GXM_TextureData *vita_texture = (VITA_GXM_TextureData *) texture->driverdata; Uint8 *dst; - int row, length,dpitch; - src = pixels; + int row, length, dpitch; + +#if SDL_HAVE_YUV + if (vita_texture->yuv || vita_texture->nv12) { + VITA_GXM_SetYUVProfile(renderer, texture); + } +#endif VITA_GXM_LockTexture(renderer, texture, rect, (void **)&dst, &dpitch); length = rect->w * SDL_BYTESPERPIXEL(texture->format); if (length == pitch && length == dpitch) { - SDL_memcpy(dst, src, length*rect->h); + SDL_memcpy(dst, pixels, length*rect->h); } else { for (row = 0; row < rect->h; ++row) { - SDL_memcpy(dst, src, length); - src += pitch; + SDL_memcpy(dst, pixels, length); + pixels += pitch; dst += dpitch; } } +#if SDL_HAVE_YUV + if (vita_texture->yuv) { + void *Udst; + void *Vdst; + int uv_pitch = (dpitch+1) / 2; + int uv_src_pitch = (pitch+1) / 2; + SDL_Rect UVrect = {rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2}; + + // skip Y plane + Uint8 *Dpixels = gxm_texture_get_datap(vita_texture->tex) + (vita_texture->pitch * vita_texture->h); + + Udst = Dpixels + (UVrect.y * uv_pitch) + UVrect.x; + Vdst = Dpixels + (uv_pitch * ((vita_texture->h + 1) / 2)) + (UVrect.y * uv_pitch) + UVrect.x; + + length = UVrect.w; + + // U plane + if (length == uv_src_pitch && length == uv_pitch) { + SDL_memcpy(Udst, pixels, length*UVrect.h); + } else { + for (row = 0; row < UVrect.h; ++row) { + SDL_memcpy(Udst, pixels, length); + pixels += uv_src_pitch; + Udst += uv_pitch; + } + } + + // V plane + if (length == uv_src_pitch && length == uv_pitch) { + SDL_memcpy(Vdst, pixels, length*UVrect.h); + } else { + for (row = 0; row < UVrect.h; ++row) { + SDL_memcpy(Vdst, pixels, length); + pixels += uv_src_pitch; + Vdst += uv_pitch; + } + } + + } else if (vita_texture->nv12) { + void *UVdst; + int uv_pitch = 2 * ((dpitch+1) / 2); + int uv_src_pitch = 2 * ((pitch+1) / 2); + SDL_Rect UVrect = {rect->x / 2, rect->y / 2, (rect->w + 1) / 2 , (rect->h + 1) / 2}; + + // skip Y plane + void *Dpixels = (void *) ((Uint8 *) gxm_texture_get_datap(vita_texture->tex) + (vita_texture->pitch * vita_texture->h)); + UVdst = Dpixels + (UVrect.y * uv_pitch) + UVrect.x; + + length = UVrect.w*2; + + // UV plane + if (length == uv_src_pitch && length == uv_pitch) { + SDL_memcpy(UVdst, pixels, length*UVrect.h); + } else { + for (row = 0; row < UVrect.h; ++row) { + SDL_memcpy(UVdst, pixels, length); + pixels += uv_src_pitch; + UVdst += uv_pitch; + } + } + } +#endif + return 0; } +#if SDL_HAVE_YUV static int VITA_GXM_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture, const SDL_Rect * rect, @@ -345,9 +465,133 @@ VITA_GXM_UpdateTextureYUV(SDL_Renderer * renderer, SDL_Texture * texture, const Uint8 *Uplane, int Upitch, const Uint8 *Vplane, int Vpitch) { + Uint8 *dst; + int row, length, dpitch; + SDL_Rect UVrect = {rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2}; + + VITA_GXM_SetYUVProfile(renderer, texture); + + // copy Y plane + // obtain pixels via locking so that texture is flushed + VITA_GXM_LockTexture(renderer, texture, rect, (void **)&dst, &dpitch); + + length = rect->w; + + if (length == Ypitch && length == dpitch) { + SDL_memcpy(dst, Yplane, length*rect->h); + } else { + for (row = 0; row < rect->h; ++row) { + SDL_memcpy(dst, Yplane, length); + Yplane += Ypitch; + dst += dpitch; + } + } + + // U/V planes + { + void *Udst; + void *Vdst; + VITA_GXM_TextureData *vita_texture = (VITA_GXM_TextureData *) texture->driverdata; + int uv_pitch = (dpitch+1) / 2; + + // skip Y plane + void *pixels = (void *) ((Uint8 *) gxm_texture_get_datap(vita_texture->tex) + (vita_texture->pitch * vita_texture->h)); + + if (texture->format == SDL_PIXELFORMAT_YV12) { // YVU + Vdst = pixels + (UVrect.y * uv_pitch) + UVrect.x; + Udst = pixels + (uv_pitch * ((vita_texture->h + 1) / 2)) + (UVrect.y * uv_pitch) + UVrect.x; + } else { // YUV + Udst = pixels + (UVrect.y * uv_pitch) + UVrect.x; + Vdst = pixels + (uv_pitch * ((vita_texture->h + 1) / 2)) + (UVrect.y * uv_pitch) + UVrect.x; + } + + length = UVrect.w; + + // U plane + if (length == Upitch && length == uv_pitch) { + SDL_memcpy(Udst, Uplane, length*UVrect.h); + } else { + for (row = 0; row < UVrect.h; ++row) { + SDL_memcpy(Udst, Uplane, length); + Uplane += Upitch; + Udst += uv_pitch; + } + } + + // V plane + if (length == Vpitch && length == uv_pitch) { + SDL_memcpy(Vdst, Vplane, length*UVrect.h); + } else { + for (row = 0; row < UVrect.h; ++row) { + SDL_memcpy(Vdst, Vplane, length); + Vplane += Vpitch; + Vdst += uv_pitch; + } + } + + } + return 0; } +static int +VITA_GXM_UpdateTextureNV(SDL_Renderer * renderer, SDL_Texture * texture, + const SDL_Rect * rect, + const Uint8 *Yplane, int Ypitch, + const Uint8 *UVplane, int UVpitch) +{ + + Uint8 *dst; + int row, length, dpitch; + SDL_Rect UVrect = {rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2}; + + VITA_GXM_SetYUVProfile(renderer, texture); + + // copy Y plane + VITA_GXM_LockTexture(renderer, texture, rect, (void **)&dst, &dpitch); + + length = rect->w * SDL_BYTESPERPIXEL(texture->format); + + if (length == Ypitch && length == dpitch) { + SDL_memcpy(dst, Yplane, length*rect->h); + } else { + for (row = 0; row < rect->h; ++row) { + SDL_memcpy(dst, Yplane, length); + Yplane += Ypitch; + dst += dpitch; + } + } + + // UV plane + { + void *UVdst; + VITA_GXM_TextureData *vita_texture = (VITA_GXM_TextureData *) texture->driverdata; + int uv_pitch = 2 * ((dpitch+1) / 2); + + // skip Y plane + void *pixels = (void *) ((Uint8 *) gxm_texture_get_datap(vita_texture->tex) + (vita_texture->pitch * vita_texture->h)); + + UVdst = pixels + (UVrect.y * uv_pitch) + UVrect.x; + + length = UVrect.w * 2; + + // UV plane + if (length == UVpitch && length == uv_pitch) { + SDL_memcpy(UVdst, UVplane, length*UVrect.h); + } else { + for (row = 0; row < UVrect.h; ++row) { + SDL_memcpy(UVdst, UVplane, length); + UVplane += UVpitch; + UVdst += uv_pitch; + } + } + } + + return 0; +} + +#endif + static int VITA_GXM_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, void **pixels, int *pitch) @@ -512,6 +756,7 @@ VITA_GXM_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Textu float scale_x, float scale_y) { VITA_GXM_RenderData *data = (VITA_GXM_RenderData *) renderer->driverdata; + VITA_GXM_TextureData* vita_texture = (VITA_GXM_TextureData*) texture->driverdata; int i; int count = indices ? num_indices : num_vertices; @@ -551,7 +796,7 @@ VITA_GXM_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Textu vertices[i].x = xy_[0] * scale_x; vertices[i].y = xy_[1] * scale_y; - vertices[i].u = uv_[0]; + vertices[i].u = uv_[0] * vita_texture->wscale; vertices[i].v = uv_[1]; vertices[i].color = col_; } @@ -730,14 +975,6 @@ SetDrawState(VITA_GXM_RenderData *data, const SDL_RenderCommand *cmd) return 0; } -static int -SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd) -{ - VITA_GXM_RenderData *data = (VITA_GXM_RenderData *) renderer->driverdata; - - return SetDrawState(data, cmd); -} - static int VITA_GXM_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) { @@ -824,11 +1061,7 @@ VITA_GXM_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void * nextcmd = nextcmd->next; } - if (thistexture) { - ret = SetCopyState(renderer, cmd); - } else { - ret = SetDrawState(data, cmd); - } + ret = SetDrawState(data, cmd); if (ret == 0) { int op = SCE_GXM_PRIMITIVE_TRIANGLES; diff --git a/src/render/vitagxm/SDL_render_vita_gxm_tools.c b/src/render/vitagxm/SDL_render_vita_gxm_tools.c index e043b55ab..0ecae3839 100644 --- a/src/render/vitagxm/SDL_render_vita_gxm_tools.c +++ b/src/render/vitagxm/SDL_render_vita_gxm_tools.c @@ -117,6 +117,8 @@ tex_format_to_bytespp(SceGxmTextureFormat format) case SCE_GXM_TEXTURE_BASE_FORMAT_U8: case SCE_GXM_TEXTURE_BASE_FORMAT_S8: case SCE_GXM_TEXTURE_BASE_FORMAT_P8: + case SCE_GXM_TEXTURE_BASE_FORMAT_YUV420P2: // YUV actually uses 12 bits per pixel. UV planes bits/mem are handled elsewhere + case SCE_GXM_TEXTURE_BASE_FORMAT_YUV420P3: return 1; case SCE_GXM_TEXTURE_BASE_FORMAT_U4U4U4U4: case SCE_GXM_TEXTURE_BASE_FORMAT_U8U3U3U2: @@ -995,25 +997,6 @@ gxm_texture_get_format(const gxm_texture *texture) return sceGxmTextureGetFormat(&texture->gxm_tex); } -unsigned int -gxm_texture_get_width(const gxm_texture *texture) -{ - return sceGxmTextureGetWidth(&texture->gxm_tex); -} - -unsigned int -gxm_texture_get_height(const gxm_texture *texture) -{ - return sceGxmTextureGetHeight(&texture->gxm_tex); -} - -unsigned int -gxm_texture_get_stride(const gxm_texture *texture) -{ - return ((gxm_texture_get_width(texture) + 7) & ~7) - * tex_format_to_bytespp(gxm_texture_get_format(texture)); -} - void * gxm_texture_get_datap(const gxm_texture *texture) { @@ -1021,15 +1004,34 @@ gxm_texture_get_datap(const gxm_texture *texture) } gxm_texture * -create_gxm_texture(VITA_GXM_RenderData *data, unsigned int w, unsigned int h, SceGxmTextureFormat format, unsigned int isRenderTarget) +create_gxm_texture(VITA_GXM_RenderData *data, unsigned int w, unsigned int h, SceGxmTextureFormat format, unsigned int isRenderTarget, unsigned int *return_w, unsigned int *return_h, unsigned int *return_pitch, float *return_wscale) { gxm_texture *texture = SDL_calloc(1, sizeof(gxm_texture)); - const int tex_size = ((w + 7) & ~ 7) * h * tex_format_to_bytespp(format); + int aligned_w = ALIGN(w, 8); + int texture_w = w; + int tex_size = aligned_w * h * tex_format_to_bytespp(format); void *texture_data; + int ret; + + *return_wscale = 1.0f; + + // SCE_GXM_TEXTURE_BASE_FORMAT_YUV420P3/P2 based formats require width aligned to 16 + if ( (format & 0x9f000000U) == SCE_GXM_TEXTURE_BASE_FORMAT_YUV420P3 || (format & 0x9f000000U) == SCE_GXM_TEXTURE_BASE_FORMAT_YUV420P2) { + aligned_w = ALIGN(w, 16); + texture_w = aligned_w; + tex_size = aligned_w * h * tex_format_to_bytespp(format); + *return_wscale = (float) (w) / texture_w; + // add storage for UV planes + tex_size += (((aligned_w + 1) / 2) * ((h + 1) / 2)) * 2; + } if (!texture) return NULL; + *return_w = w; + *return_h = h; + *return_pitch = aligned_w * tex_format_to_bytespp(format); + /* Allocate a GPU buffer for the texture */ texture_data = mem_gpu_alloc( SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW, @@ -1060,7 +1062,12 @@ create_gxm_texture(VITA_GXM_RenderData *data, unsigned int w, unsigned int h, Sc SDL_memset(texture_data, 0, tex_size); /* Create the gxm texture */ - sceGxmTextureInitLinear( &texture->gxm_tex, texture_data, format, w, h, 0); + ret = sceGxmTextureInitLinear( &texture->gxm_tex, texture_data, format, texture_w, h, 0); + if (ret < 0) { + free_gxm_texture(texture); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "texture init failed: %x\n", ret); + return NULL; + } if (isRenderTarget) { void *depthBufferData; @@ -1084,7 +1091,7 @@ create_gxm_texture(VITA_GXM_RenderData *data, unsigned int w, unsigned int h, Sc if (err < 0) { free_gxm_texture(texture); - SDL_LogError(SDL_LOG_CATEGORY_RENDER, "color surface init failed: %d\n", err); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "color surface init failed: %x\n", err); return NULL; } @@ -1107,7 +1114,7 @@ create_gxm_texture(VITA_GXM_RenderData *data, unsigned int w, unsigned int h, Sc if (err < 0) { free_gxm_texture(texture); - SDL_LogError(SDL_LOG_CATEGORY_RENDER, "depth stencil init failed: %d\n", err); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "depth stencil init failed: %x\n", err); return NULL; } @@ -1132,7 +1139,7 @@ create_gxm_texture(VITA_GXM_RenderData *data, unsigned int w, unsigned int h, Sc if (err < 0) { free_gxm_texture(texture); - SDL_LogError(SDL_LOG_CATEGORY_RENDER, "create render target failed: %d\n", err); + SDL_LogError(SDL_LOG_CATEGORY_RENDER, "create render target failed: %x\n", err); return NULL; } } diff --git a/src/render/vitagxm/SDL_render_vita_gxm_tools.h b/src/render/vitagxm/SDL_render_vita_gxm_tools.h index 351bdc2e6..8e9caeb21 100644 --- a/src/render/vitagxm/SDL_render_vita_gxm_tools.h +++ b/src/render/vitagxm/SDL_render_vita_gxm_tools.h @@ -48,15 +48,12 @@ void unset_clip_rectangle(VITA_GXM_RenderData *data); int gxm_init(SDL_Renderer *renderer); void gxm_finish(SDL_Renderer *renderer); -gxm_texture *create_gxm_texture(VITA_GXM_RenderData *data, unsigned int w, unsigned int h, SceGxmTextureFormat format, unsigned int isRenderTarget); +gxm_texture *create_gxm_texture(VITA_GXM_RenderData *data, unsigned int w, unsigned int h, SceGxmTextureFormat format, unsigned int isRenderTarget, unsigned int *return_w, unsigned int *return_h, unsigned int *return_pitch, float *return_wscale); void free_gxm_texture(gxm_texture *texture); void gxm_texture_set_filters(gxm_texture *texture, SceGxmTextureFilter min_filter, SceGxmTextureFilter mag_filter); SceGxmTextureFormat gxm_texture_get_format(const gxm_texture *texture); -unsigned int gxm_texture_get_width(const gxm_texture *texture); -unsigned int gxm_texture_get_height(const gxm_texture *texture); -unsigned int gxm_texture_get_stride(const gxm_texture *texture); void *gxm_texture_get_datap(const gxm_texture *texture); void gxm_minimal_init_for_common_dialog(void); diff --git a/src/render/vitagxm/SDL_render_vita_gxm_types.h b/src/render/vitagxm/SDL_render_vita_gxm_types.h index 898a92d9c..2bf8d3585 100644 --- a/src/render/vitagxm/SDL_render_vita_gxm_types.h +++ b/src/render/vitagxm/SDL_render_vita_gxm_types.h @@ -191,9 +191,12 @@ typedef struct typedef struct { gxm_texture *tex; - unsigned int pitch; - unsigned int w; - unsigned int h; + unsigned int pitch; + unsigned int w; + unsigned int h; + float wscale; + SDL_bool yuv; + SDL_bool nv12; } VITA_GXM_TextureData; #endif /* SDL_RENDER_VITA_GXM_TYPES_H */