fix for wrapping artefact when display_mode=ALWAYS
[open-ps2-loader.git] / src / renderman.c
blobd30c70a28d67a0ff22157f450a5327a8158ce5e4
1 /*
2 Copyright 2010, Volca
3 Licenced under Academic Free License version 3.0
4 Review OpenUsbLd README & LICENSE files for further details.
5 */
7 #include <stdio.h>
8 #include <kernel.h>
10 #include "include/renderman.h"
11 #include "include/ioman.h"
12 #include "include/usbld.h"
14 // Allocateable space in vram, as indicated in GsKit's code
15 #define __VRAM_SIZE 4194304
17 GSGLOBAL *gsGlobal;
18 s32 guiThreadID;
20 /** Helper texture list */
21 struct rm_texture_list_t {
22 GSTEXTURE *txt;
23 GSCLUT *clut;
24 struct rm_texture_list_t *next;
27 static struct rm_texture_list_t *uploadedTextures = NULL;
29 static int order;
30 static int vsync = 1;
31 static enum rm_vmode vmode = RM_VMODE_AUTO;
33 #define NUM_RM_VMODES 3
35 // RM Vmode -> GS Vmode conversion table
36 static int rm_mode_table[NUM_RM_VMODES] = {
37 -1, // AUTO
38 GS_MODE_PAL, // PAL
39 GS_MODE_NTSC // NTSC
42 static int rm_height_table[] = {
43 -1, // AUTO
44 512, // PAL
45 448, // NTSC
48 static float aspectWidth;
49 static float aspectHeight;
51 // Transposition values - all rendering can be transposed (moved on screen) by these
52 static float transX = 0;
53 static float transY = 0;
55 const u64 gColWhite = GS_SETREG_RGBA(0xFF,0xFF,0xFF,0x00);
56 const u64 gColBlack = GS_SETREG_RGBA(0x00,0x00,0x00,0x00);
57 const u64 gColDarker = GS_SETREG_RGBA(0x00,0x00,0x00,0x60);
58 const u64 gColFocus = GS_SETREG_RGBA(0xFF,0xFF,0xFF,0x50);
60 const u64 gDefaultCol = GS_SETREG_RGBA(0x80,0x80,0x80,0x80);
61 const u64 gDefaultAlpha = GS_SETREG_ALPHA(0,1,0,1,0);
63 static float shiftYVal;
64 static float (*shiftY)(float posY);
66 static float shiftYFunc(float posY) {
67 //return (int) ceil(shiftYVal * posY);
68 return (int) (shiftYVal * posY);
71 static float identityFunc(float posY) {
72 return posY;
75 static void rmAppendUploadedTextures(GSTEXTURE *txt) {
76 struct rm_texture_list_t *entry = (struct rm_texture_list_t *)malloc(sizeof(struct rm_texture_list_t));
77 entry->clut = NULL;
78 entry->txt = txt;
79 entry->next = uploadedTextures;
80 uploadedTextures = entry;
83 static void rmAppendUploadedCLUTs(GSCLUT *clut) {
84 struct rm_texture_list_t *entry = (struct rm_texture_list_t *)malloc(sizeof(struct rm_texture_list_t));
85 entry->txt = NULL;
86 entry->clut = clut;
87 entry->next = uploadedTextures;
88 uploadedTextures = entry;
91 static int rmClutSize(GSCLUT *clut, u32 *size, u32 *w, u32 *h) {
92 switch (clut->PSM) {
93 case GS_PSM_T4:
94 *w = 8;
95 *h = 2;
96 break;
97 case GS_PSM_T8:
98 *w = 16;
99 *h = 16;
100 break;
101 default:
102 return 0;
105 switch(clut->ClutPSM) {
106 case GS_PSM_CT32:
107 *size = (*w) * (*h) * 4;
108 break;
109 case GS_PSM_CT24:
110 *size = (*w) * (*h) * 4;
111 break;
112 case GS_PSM_CT16:
113 *size = (*w) * (*h) * 2;
114 break;
115 case GS_PSM_CT16S:
116 *size = (*w) * (*h) * 2;
117 break;
118 default:
119 return 0;
122 return 1;
125 static int rmUploadClut(GSCLUT *clut) {
126 if (clut->VramClut && clut->VramClut != GSKIT_ALLOC_ERROR) // already uploaded
127 return 1;
129 u32 size;
130 u32 w, h;
132 if (!rmClutSize(clut, &size, &w, &h))
133 return 0;
135 size = (-GS_VRAM_BLOCKSIZE_256)&(size+GS_VRAM_BLOCKSIZE_256-1);
137 // too large to fit VRAM with the currently allocated space?
138 if(gsGlobal->CurrentPointer + size >= __VRAM_SIZE)
141 if (size >= __VRAM_SIZE) {
142 // Only log this if the allocation is too large itself
143 LOG("RENDERMAN Requested clut allocation is bigger than VRAM!\n");
144 // We won't allocate this, it's too large
145 clut->VramClut = GSKIT_ALLOC_ERROR;
146 return 0;
149 rmFlush();
152 clut->VramClut = gsGlobal->CurrentPointer;
153 gsGlobal->CurrentPointer += size;
155 rmAppendUploadedCLUTs(clut);
157 gsKit_texture_send(clut->Clut, w, h, clut->VramClut, clut->ClutPSM, 1, GS_CLUT_PALLETE);
158 return 1;
161 static int rmUploadTexture(GSTEXTURE* txt) {
162 // For clut based textures...
163 if (txt->Clut) {
164 // upload CLUT first
165 if (!rmUploadClut((GSCLUT *)txt->Clut))
166 return 0;
168 // copy the new VramClut
169 txt->VramClut = ((GSCLUT*)txt->Clut)->VramClut;
172 u32 size = gsKit_texture_size(txt->Width, txt->Height, txt->PSM);
173 // alignment of the allocation
174 size = (-GS_VRAM_BLOCKSIZE_256)&(size+GS_VRAM_BLOCKSIZE_256-1);
176 // too large to fit VRAM with the currently allocated space?
177 if(gsGlobal->CurrentPointer + size >= __VRAM_SIZE)
180 if (size >= __VRAM_SIZE) {
181 // Only log this if the allocation is too large itself
182 LOG("RENDERMAN Requested texture allocation is bigger than VRAM!\n");
183 // We won't allocate this, it's too large
184 txt->Vram = GSKIT_ALLOC_ERROR;
185 return 0;
188 rmFlush();
190 // Should not flush CLUT away. If this happenned we have to reupload
191 if (txt->Clut) {
192 if (!rmUploadClut((GSCLUT *)txt->Clut))
193 return 0;
195 txt->VramClut = ((GSCLUT*)txt->Clut)->VramClut;
198 // only could fit CLUT but not the pixmap with it!
199 if(gsGlobal->CurrentPointer + size >= __VRAM_SIZE)
200 return 0;
203 txt->Vram = gsGlobal->CurrentPointer;
204 gsGlobal->CurrentPointer += size;
206 rmAppendUploadedTextures(txt);
208 // We can't do gsKit_texture_upload since it'd assume txt->Clut is the CLUT table directly
209 // whereas we're using it as a pointer to our structure containg clut data
210 gsKit_setup_tbw(txt);
211 gsKit_texture_send(txt->Mem, txt->Width, txt->Height, txt->Vram, txt->PSM, txt->TBW, txt->Clut ? GS_CLUT_TEXTURE : GS_CLUT_NONE);
213 return 1;
216 int rmPrepareTexture(GSTEXTURE* txt) {
217 if (txt->Vram && txt->Vram != GSKIT_ALLOC_ERROR) // already uploaded
218 return 1;
220 return rmUploadTexture(txt);
223 void rmDispatch(void) {
224 gsKit_queue_exec(gsGlobal);
228 void rmFlush(void) {
229 rmDispatch();
231 // release all the uploaded textures
232 gsKit_vram_clear(gsGlobal);
234 while (uploadedTextures) {
235 // free clut and txt if those are filled in
236 if (uploadedTextures->txt) {
237 uploadedTextures->txt->Vram = 0;
238 uploadedTextures->txt->VramClut = 0;
241 if (uploadedTextures->clut)
242 uploadedTextures->clut->VramClut = 0;
244 struct rm_texture_list_t *entry = uploadedTextures;
245 uploadedTextures = uploadedTextures->next;
246 free(entry);
250 void rmStartFrame(void) {
251 order = 0;
254 void rmEndFrame(void) {
255 gsKit_set_finish(gsGlobal);
257 rmFlush();
259 // Wait for draw ops to finish
260 gsKit_finish();
262 if(!gsGlobal->FirstFrame)
264 if (vsync)
265 SleepThread();
267 if(gsGlobal->DoubleBuffering == GS_SETTING_ON)
269 GS_SET_DISPFB2( gsGlobal->ScreenBuffer[gsGlobal->ActiveBuffer & 1] / 8192,
270 gsGlobal->Width / 64, gsGlobal->PSM, 0, 0 );
272 gsGlobal->ActiveBuffer ^= 1;
273 gsGlobal->PrimContext ^= 1;
278 gsKit_setactive(gsGlobal);
281 static int rmOnVSync(void) {
282 if (vsync)
283 iWakeupThread(guiThreadID);
285 return 0;
288 void rmInit() {
289 gsGlobal = gsKit_init_global();
291 rm_mode_table[RM_VMODE_AUTO] = gsGlobal->Mode;
292 rm_height_table[RM_VMODE_AUTO] = gsGlobal->Height;
294 dmaKit_init(D_CTRL_RELE_OFF, D_CTRL_MFD_OFF, D_CTRL_STS_UNSPEC,
295 D_CTRL_STD_OFF, D_CTRL_RCYC_8, 1 << DMA_CHANNEL_GIF);
297 // Initialize the DMAC
298 dmaKit_chan_init(DMA_CHANNEL_GIF);
299 dmaKit_chan_init(DMA_CHANNEL_FROMSPR);
300 dmaKit_chan_init(DMA_CHANNEL_TOSPR);
302 rmSetMode(1);
304 order = 0;
306 aspectWidth = 1.0f;
307 aspectHeight = 1.0f;
309 shiftYVal = 1.0f;
310 shiftY = &shiftYFunc;
312 transX = 0.0f;
313 transY = 0.0f;
315 guiThreadID = GetThreadId();
316 gsKit_add_vsync_handler(&rmOnVSync);
319 int rmSetMode(int force) {
320 if (gVMode < RM_VMODE_AUTO || gVMode >= NUM_RM_VMODES)
321 gVMode = RM_VMODE_AUTO;
323 // we don't want to set the vmode without a reason...
324 int changed = (vmode != gVMode || vsync != gVSync || force);
325 if (changed) {
326 vmode = gVMode;
327 vsync = gVSync;
329 gsGlobal->Mode = rm_mode_table[vmode];
330 gsGlobal->Height = rm_height_table[vmode];
332 gsGlobal->PSM = GS_PSM_CT24;
333 gsGlobal->PSMZ = GS_PSMZ_16S;
334 gsGlobal->ZBuffering = GS_SETTING_OFF;
335 gsGlobal->PrimAlphaEnable = GS_SETTING_ON;
336 gsGlobal->DoubleBuffering = GS_SETTING_ON;
338 gsKit_init_screen(gsGlobal);
340 gsKit_mode_switch(gsGlobal, GS_ONESHOT);
342 gsKit_set_test(gsGlobal, GS_ZTEST_OFF);
344 // reset the contents of the screen to avoid garbage being displayed
345 gsKit_clear(gsGlobal, gColBlack);
346 gsKit_sync_flip(gsGlobal);
348 LOG("RENDERMAN New vmode: %d, %d x %d\n", vmode, gsGlobal->Width, gsGlobal->Height);
350 return changed;
353 void rmGetScreenExtents(int *w, int *h) {
354 *w = gsGlobal->Width;
355 *h = gsGlobal->Height;
358 void rmEnd(void) {
359 rmFlush();
362 /** If txt is null, don't use DIM_UNDEF size */
363 void rmSetupQuad(GSTEXTURE* txt, int x, int y, short aligned, int w, int h, short scaled, u64 color, rm_quad_t* q) {
364 if (aligned) {
365 float dim;
366 if (w == DIM_UNDEF)
367 w = txt->Width;
368 if (h == DIM_UNDEF)
369 h = txt->Height;
371 if (scaled)
372 dim = aspectWidth * (w >> 1);
373 else
374 dim = w >> 1;
375 q->ul.x = x - dim;
376 q->br.x = x + dim;
378 if (scaled)
379 dim = aspectHeight * (h >> 1);
380 else
381 dim = h >> 1;
382 q->ul.y = shiftY(y) - dim;
383 q->br.y = shiftY(y) + dim;
384 } else {
385 if (w == DIM_UNDEF)
386 w = txt->Width;
387 if (h == DIM_UNDEF)
388 h = txt->Height;
390 q->ul.x = x;
391 if (scaled)
392 q->br.x = x + aspectWidth * w;
393 else
394 q->br.x = x + w;
396 q->ul.y = shiftY(y);
397 if (scaled)
398 q->br.y = shiftY(y) + aspectHeight * h;
399 else
400 q->br.y = shiftY(y) + h;
403 q->color = color;
405 if (txt) {
406 q->txt = txt;
407 q->ul.u = 0;
408 q->ul.v = 0;
409 q->br.u = txt->Width;
410 q->br.v = txt->Height;
414 void rmDrawQuad(rm_quad_t* q) { // NO scaling, NO shift, NO alignment
415 if (!rmPrepareTexture(q->txt)) // won't render if not ready!
416 return;
418 if ((q->txt->PSM == GS_PSM_CT32) || (q->txt->Clut && q->txt->ClutPSM == GS_PSM_CT32))
420 gsKit_set_primalpha(gsGlobal, gDefaultAlpha, 0);
423 gsKit_prim_sprite_texture(gsGlobal, q->txt,
424 q->ul.x + transX, q->ul.y + transY,
425 q->ul.u, q->ul.v,
426 q->br.x + transX, q->br.y + transY,
427 q->br.u, q->br.v, order, q->color);
428 order++;
430 gsKit_set_primalpha(gsGlobal, GS_BLEND_BACK2FRONT, 0);
433 void rmDrawPixmap(GSTEXTURE* txt, int x, int y, short aligned, int w, int h, short scaled, u64 color) {
434 rm_quad_t quad;
435 rmSetupQuad(txt, x, y, aligned, w, h, scaled, color, &quad);
436 rmDrawQuad(&quad);
439 void rmDrawOverlayPixmap(GSTEXTURE* overlay, int x, int y, short aligned, int w, int h, short scaled, u64 color,
440 GSTEXTURE* inlay, int ulx, int uly, int urx, int ury, int blx, int bly, int brx, int bry) {
442 rm_quad_t quad;
443 rmSetupQuad(overlay, x, y, aligned, w, h, scaled, color, &quad);
445 if (!rmPrepareTexture(inlay))
446 return;
448 if (inlay->PSM == GS_PSM_CT32)
449 gsKit_set_primalpha(gsGlobal, gDefaultAlpha, 0);
451 gsKit_prim_quad_texture(gsGlobal, inlay, quad.ul.x + transX + aspectWidth * ulx, quad.ul.y + transY + uly, 0, 0,
452 quad.ul.x + transX + aspectWidth * urx, quad.ul.y + transY + ury, inlay->Width, 0,
453 quad.ul.x + transX + aspectWidth * blx, quad.ul.y + transY + bly, 0, inlay->Height,
454 quad.ul.x + transX + aspectWidth * brx, quad.ul.y + transY + bry, inlay->Width, inlay->Height, order, gDefaultCol);
455 order++;
456 gsKit_set_primalpha(gsGlobal, GS_BLEND_BACK2FRONT, 0);
458 rmDrawQuad(&quad);
461 void rmDrawRect(int x, int y, int w, int h, u64 color) {
462 gsKit_set_primalpha(gsGlobal, GS_SETREG_ALPHA(0,1,0,1,0), 0);
463 gsKit_prim_quad(gsGlobal, x + transX, shiftY(y) + transY, x + w + transX, shiftY(y) + transY, x + transX, shiftY(y) + h + transY, x + w + transX, shiftY(y) + h + transY, order, color);
464 order++;
465 gsKit_set_primalpha(gsGlobal, GS_BLEND_BACK2FRONT, 0);
468 void rmDrawLine(int x, int y, int x1, int y1, u64 color) {
469 gsKit_prim_line(gsGlobal, x + transX, shiftY(y) + transY, x1 + transX, shiftY(y1) + transY, order, color);
472 void rmSetAspectRatio(float width, float height) {
473 aspectWidth = width;
474 aspectHeight = height;
477 void rmResetAspectRatio() {
478 aspectWidth = 1.0f;
479 aspectHeight = 1.0f;
482 void rmGetAspectRatio(float *w, float *h) {
483 *w = aspectWidth;
484 *h = aspectHeight;
487 void rmApplyAspectRatio(int* w, int* h) {
488 *w = *w * aspectWidth;
489 *h = *h * aspectHeight;
492 void rmSetShiftRatio(float shiftYRatio) {
493 shiftYVal = shiftYRatio;
494 shiftY = &shiftYFunc;
497 void rmResetShiftRatio() {
498 shiftY = &identityFunc;
501 void rmApplyShiftRatio(int* y) {
502 *y = shiftY(*y);
505 void rmSetTransposition(float x, float y) {
506 transX = x;
507 transY = y;