@@ -45,6 +45,7 @@ typedef struct
4545 bool vsync ;
4646 u8 efb_pixel_format ;
4747 SDL_Texture * render_target ;
48+ SDL_Texture * saved_efb_texture ;
4849} OGC_RenderData ;
4950
5051typedef struct
@@ -58,6 +59,8 @@ typedef struct
5859 u8 needed_stages ; // Normally 1, set to 2 for palettized formats
5960} OGC_TextureData ;
6061
62+ static void OGC_DestroyTexture (SDL_Renderer * renderer , SDL_Texture * texture );
63+
6164static void OGC_WindowEvent (SDL_Renderer * renderer , const SDL_WindowEvent * event )
6265{
6366}
@@ -102,6 +105,38 @@ static inline void OGC_SetBlendMode(SDL_Renderer *renderer, SDL_BlendMode blend_
102105 set_blend_mode_real (renderer , blend_mode );
103106}
104107
108+ static void load_efb_from_texture (SDL_Renderer * renderer , SDL_Texture * texture )
109+ {
110+ OGC_TextureData * ogc_tex = texture -> driverdata ;
111+
112+ OGC_load_texture (ogc_tex -> texels , texture -> w , texture -> h ,
113+ ogc_tex -> format , SDL_ScaleModeNearest );
114+
115+ GX_ClearVtxDesc ();
116+ GX_SetVtxDesc (GX_VA_POS , GX_DIRECT );
117+ GX_SetVtxAttrFmt (GX_VTXFMT0 , GX_VA_POS , GX_POS_XY , GX_S16 , 0 );
118+
119+ GX_SetVtxDesc (GX_VA_TEX0 , GX_DIRECT );
120+ GX_SetVtxAttrFmt (GX_VTXFMT0 , GX_VA_TEX0 , GX_TEX_ST , GX_U8 , 0 );
121+ GX_SetNumTexGens (1 );
122+
123+ GX_SetTexCoordGen (GX_TEXCOORD0 , GX_TG_MTX2x4 , GX_TG_TEX0 , GX_IDENTITY );
124+ GX_SetTevOrder (GX_TEVSTAGE0 , GX_TEXCOORD0 , GX_TEXMAP0 , GX_COLOR0A0 );
125+ GX_SetTevOp (GX_TEVSTAGE0 , GX_REPLACE );
126+ GX_SetNumTevStages (1 );
127+
128+ GX_Begin (GX_QUADS , GX_VTXFMT0 , 4 );
129+ GX_Position2s16 (0 , 0 );
130+ GX_TexCoord2u8 (0 , 0 );
131+ GX_Position2s16 (texture -> w , 0 );
132+ GX_TexCoord2u8 (1 , 0 );
133+ GX_Position2s16 (texture -> w , texture -> h );
134+ GX_TexCoord2u8 (1 , 1 );
135+ GX_Position2s16 (0 , texture -> h );
136+ GX_TexCoord2u8 (0 , 1 );
137+ GX_End ();
138+ }
139+
105140static void save_efb_to_texture (SDL_Texture * texture )
106141{
107142 OGC_TextureData * ogc_tex = texture -> driverdata ;
@@ -192,6 +227,42 @@ static void OGC_SetTextureScaleMode(SDL_Renderer *renderer,
192227 * loading it in OGC_load_texture(). */
193228}
194229
230+ static SDL_Texture * create_efb_texture (OGC_RenderData * data , SDL_Texture * target )
231+ {
232+ /* Note: we do return a SDL_Texture, but not via SDL's API, since that does
233+ * a bunch of other stuffs we don't care about. We create this texture for
234+ * our internal use, so we initialize only those fields we care about. */
235+ SDL_Texture * texture ;
236+ OGC_TextureData * ogc_tex ;
237+ u32 texture_size ;
238+
239+ texture = SDL_calloc (1 , sizeof (* texture ));
240+ if (!texture ) goto fail_texture_alloc ;
241+
242+ ogc_tex = SDL_calloc (1 , sizeof (OGC_TextureData ));
243+ if (!ogc_tex ) goto fail_ogc_tex_alloc ;
244+
245+ ogc_tex -> format = data -> efb_pixel_format == GX_PF_RGB565_Z16 ?
246+ GX_TF_RGB565 : GX_TF_RGBA8 ;
247+ texture -> w = target -> w ;
248+ texture -> h = target -> h ;
249+ texture_size = GX_GetTexBufferSize (texture -> w , texture -> h , ogc_tex -> format ,
250+ GX_FALSE , 0 );
251+ ogc_tex -> texels = memalign (32 , texture_size );
252+ if (!ogc_tex -> texels ) goto fail_texels_alloc ;
253+
254+ texture -> driverdata = ogc_tex ;
255+ return texture ;
256+
257+ fail_texels_alloc :
258+ SDL_free (ogc_tex -> texels );
259+ fail_ogc_tex_alloc :
260+ SDL_free (texture );
261+ fail_texture_alloc :
262+ SDL_OutOfMemory ();
263+ return NULL ;
264+ }
265+
195266static int OGC_SetRenderTarget (SDL_Renderer * renderer , SDL_Texture * texture )
196267{
197268 OGC_RenderData * data = renderer -> driverdata ;
@@ -203,16 +274,11 @@ static int OGC_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
203274 }
204275
205276 if (data -> ops_after_present > 0 ) {
206- /* We should save the current EFB contents into the window data.
207- * However, it's unclear whether this is a possible scenario, since
208- * all actual drawing happens in RunCommandQueue() and this method
209- * will not be called in between of the drawing operations; but
210- * just to be on the safe side, log a warning. We can come back to
211- * this later and implement the EFB saving if we see that this
212- * happens in real life.
213- */
214- SDL_LogWarn (SDL_LOG_CATEGORY_RENDER ,
215- "Render target set after drawing!" );
277+ /* Save the current EFB contents if we already drew something onto
278+ * it. We'll restore it later, when the rendering target is reset
279+ * to NULL (the screen). */
280+ data -> saved_efb_texture = create_efb_texture (data , texture );
281+ save_efb_to_texture (data -> saved_efb_texture );
216282 }
217283
218284 if (SDL_ISPIXELFORMAT_ALPHA (texture -> format )) {
@@ -229,6 +295,17 @@ static int OGC_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
229295 GX_SetPixelFmt (data -> efb_pixel_format , GX_ZC_LINEAR );
230296 }
231297
298+ /* Restore the EFB to how it was before the we started to render to a
299+ * texture. */
300+ if (!texture && data -> saved_efb_texture ) {
301+ load_efb_from_texture (renderer , data -> saved_efb_texture );
302+ /* Flush the draw operation before destroying the texture */
303+ GX_DrawDone ();
304+ OGC_DestroyTexture (renderer , data -> saved_efb_texture );
305+ SDL_free (data -> saved_efb_texture );
306+ data -> saved_efb_texture = NULL ;
307+ }
308+
232309 return 0 ;
233310}
234311
0 commit comments