// gdraw_psp2.h - author: Fabian Giesen - copyright 2014 RAD Game Tools // // Interface for creating a PSP2 GDraw driver. #include "gdraw.h" #define IDOC //idoc(parent,GDraw_psp2) // Size and alignment requirements of GDraw context memory. #define GDRAW_PSP2_CONTEXT_MEM_SIZE (16*1024) // Alignment requirements for different resource types (in bytes) #define GDRAW_PSP2_TEXTURE_ALIGNMENT 16 #define GDRAW_PSP2_VERTEXBUFFER_ALIGNMENT 16 typedef enum gdraw_psp2_resourcetype { GDRAW_PSP2_RESOURCE_texture, GDRAW_PSP2_RESOURCE_vertexbuffer, GDRAW_PSP2_RESOURCE__count, } gdraw_psp2_resourcetype; typedef struct { U32 allocs_attempted; // number of allocations attempted from the staging buffer U32 allocs_succeeded; // number of allocations that succeeded U32 bytes_attempted; // number of bytes attempted to allocate U32 bytes_succeeded; // number of bytes successfully allocated U32 largest_bytes_attempted; // number of bytes in largest attempted alloc U32 largest_bytes_succeeded; // number of bytes in lagrest successful alloc } gdraw_psp2_dynamic_stats; typedef struct { void *start; // pointer to the start of the buffer U32 size_in_bytes; // size of the buffer in bytes U64 sync; // used internally by GDraw for synchronization. gdraw_psp2_dynamic_stats stats; // stats on buffer usage - these are for your benefit! } gdraw_psp2_dynamic_buffer; IDOC extern void gdraw_psp2_InitDynamicBuffer(gdraw_psp2_dynamic_buffer *buffer, void *ptr, U32 num_bytes); /* Initializes a GDraw dynamic buffer struct. The "dynamic buffer" is where GDraw stores all transient data for a scene - dynamic vertex and index data, uniform buffers and the like. We wrap them in a struct so we can handle synchronization with the GPU. "ptr" should point to non-cached, GPU-mapped readable memory. "num_bytes" is the size of the buffer in bytes. */ IDOC extern void gdraw_psp2_WaitForDynamicBufferIdle(gdraw_psp2_dynamic_buffer *buffer); /* Waits until a GDraw dynamic buffer is idle, i.e. not being used by the GPU anymore. You need to call this if you intend to free the allocated storage. */ IDOC extern int gdraw_psp2_SetResourceMemory(gdraw_psp2_resourcetype type, S32 num_handles, void *ptr, S32 num_bytes); /* Sets up the resource pools that GDraw uses for its video memory management. It sets both the number of handles and the address and size of memory to use. GDraw keeps track of allocations in each pool, and will free old resources in a LRU manner to make space if one of the limits is about to be exceeded. It will also automatically defragment memory if necessary to fulfill an allocation request. "ptr" points to the address of the resource pool. This memory needs to be mapped to the GPU and *writeable*. If it isn't, the GPU will crash during either this function or CreateContext! Pass in NULL for "ptr" and zero "num_bytes" to free the memory allocated to a specific pool. GDraw can run into cases where resource memory gets fragmented; we defragment automatically in that case. However, to make this work, GDraw on PSP2 needs resource pool memory equivalent to *twice* the largest working set in any scene. So for example, if you use 10MB worth of textures, GDraw needs at least a 20MB texture pool! On other platforms, we can avoid this extra cost by draining the GPU pipeline in the middle of a frame in certain rare cases, but doing so on PSP2 would mean not supporting deferred contexts. You need to set up all of the resource pools before you can start rendering. If you modify this at runtime, you need to call IggyPlayerFlushAll on all active Iggys (if any) since this call invalidates all resource handles they currently hold. Resource pool memory has certain alignment requirements - see the #defines above. If you pass in an unaligned pointer, GDraw will automatically clip off some bytes at the front to make the buffer aligned - in other words, you get somewhat less usable bytes, but it should work fine. If any Iggy draw calls are in flight, this call will block waiting for those calls to finish (i.e. for the resource memory to become unused). */ IDOC extern void gdraw_psp2_ResetAllResourceMemory(); /* Frees all resource pools managed by GDraw. Use this as a quick way of freeing (nearly) all memory allocated by GDraw without shutting it down completely. For example, you might want to use this to quickly flush all memory allocated by GDraw when transitioning between the main menu and the game proper. Like with SetResourceMemory, you need to call IggyPlayerFlushAll on all currently active Iggy players if you do this - although we recommend that you only use this function when there aren't any. */ IDOC extern GDrawFunctions * gdraw_psp2_CreateContext(SceGxmShaderPatcher *shader_patcher, void *context_mem, volatile U32 *notification, SceGxmOutputRegisterFormat reg_format); /* Creates a GDraw context for rendering using GXM. You need to pass in a pointer to the shader patcher to use, a pointer to "context memory" which holds a few persistent resources shared by all Iggys (it needs to hold GDRAW_PSP2_CONTEXT_MEM_SIZE bytes and must be mapped to be GPU readable), and a pointer to the notificaiton to use for GDraw sync. "reg_format" specifies the output register format to specify when creating GDraw's fragment shaders. This should match your color surface. Typical choices are: - SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4 (32-bit output register size in color surface) - SCE_GXM_OUTPUT_REGISTER_FORMAT_HALF4 (64-bit output register size in color surface) There can only be one GDraw context active at any one time. The shader_patcher must be valid for as long as a GDraw context is alive. If initialization fails for some reason (the main reason would be an out of memory condition), NULL is returned. Otherwise, you can pass the return value to IggySetGDraw. */ IDOC extern void gdraw_psp2_DestroyContext(void); /* Destroys the current GDraw context, if any. If any Iggy draw calls are in flight, this call will block waiting for those calls to finish (i.e. for the resource memory to become unused). */ IDOC extern void gdraw_psp2_Begin(SceGxmContext *context, const SceGxmColorSurface *color, const SceGxmDepthStencilSurface *depth, gdraw_psp2_dynamic_buffer *dynamic_buffer); /* This sets the SceGxmContext that GDraw writes its commands to. It also specifies the dynamic buffer to use. Any GDraw / Iggy rendering calls outside a Begin / End bracket are an error and will be treated as such. The GXM context and dynamic buffer must be live during the entire Begin / End bracket. NOTE: If you are passing in a deferred context, see important notes below! GDraw uses the color and depth surfaces for parameter validation. Make sure to pass in the same values you passed in to sceGxmBeginScene. Also, inside a given scene, you may have only *one* $gdraw_psp2_Begin / $gdraw_psp2_End pair. GDraw maintains a persistent resource cache shared across all Iggys. Because of this, it is *vital* that all command lists generated from GDraw be executed in the order they were generated, or the resource pools might get corrupted. If the dynamic buffer is of insufficient size, GDraw will not be able to allocate dynamic vertex data or upload new texture/vertex buffer data during some frames, resulting in glitching! (When this happens, it will be reported as a warning, so make sure to install a warning callback). You should use multiple dynamic buffers (at least double-buffer it); otherwise, GDraw needs to stall every frame to wait for the GPU to process the previous one. GDraw starts no scenes of its own; you must call BeginScene before you issue gdraw_psp2_Begin. If you are a using a deferred context: -------------------------------------- It's allowed to pass in a deferred context as "context". You may perform Iggy rendering on a separate thread. However, because there is only a single global resource pool that is modified directly by GDraw, rendering is *not* thread-safe; that is, you may do Iggy rendering on any thread, but only a single thread may be inside a GDraw Begin / End bracket at any given time. Furthermore, if you use a deferred context, you must use (and execute!) the resulting command list and end the containing scene *before* you call $gdraw_psp2_Begin again. That is, usage must look like this: Main thread: | Other thread: | gdraw_psp2_Begin(deferred_ctx, ...) | | gdraw_psp2_End(deferred_ctx, ...) | ... | sceGxmEndCommandList(deferred_ctx, &cmd_list) | sceGxmBeginScene(...) | sceGxmExecuteCommandList(..., cmd_list) | sceGxmEndScene() | The second thread may *not* start another $gdraw_psp2_Begin before the main thread calls sceGxmEndScene(). This is inconvenient, but unfortunately required by our resource management: every GDraw Begin / End Bracket may end up having to wait for the GPU to finish work done during a previous bracket, and this only works if the corresponding jobs have been submitted to the GPU by the point $gdraw_psp2_Begin is called! */ IDOC extern SceGxmNotification gdraw_psp2_End(); /* This marks the end of GDraw rendering for a frame. It also triggers end-of-frame processing, which is important for GDraw's internal resource management. GDraw will not touch the GXM context or staging buffer after this call, so you are free to append other rendering commands after this call returns. This function will also update the "stats" field in the dynamic buffer you passed to "Begin". You are *required* to pass the returned SceGxmNotification as your "fragment notification" when calling sceGxmEndScene. This is necessary to make GDraw's resource management work! */ IDOC extern void gdraw_psp2_SetTileOrigin(S32 x, S32 y); /* This sets the x/y position of the output location of the top-left pixel of the current tile. Iggy has support for manual splitting of rendering into multiple tiles; PSP2 is already a tiled renderer that handles all this in hardware, so in practice you will probably always pass (0,0) here. You should call this inside a gdraw_psp2_Begin / gdraw_psp2_End bracket, before any Iggy / GDraw rendering takes place. */ IDOC extern void gdraw_psp2_ClearBeforeNextRender(const F32 clear_color_rgba[4]); /* You often want to clear the render target before you start Iggy rendering. This is a convenience function, if you don't want to do the clear yourself. Iggy always clears the depth/stencil buffers when it starts rendering; if you call this function first, it will also clear the color surface to the specified color during that initial clear. If this function is not called before rendering, GDraw will leave the contents of the color surface alone. This function does not do any rendering; it just sets some internal state that GDraw processes when it starts rendering. That state gets reset for every call to IggyPlayerDraw or IggyPlayerDrawTile. */ IDOC extern void RADLINK gdraw_psp2_CalculateCustomDraw_4J(IggyCustomDrawCallbackRegion *region, float matrix[16]); IDOC extern void RADLINK gdraw_psp2_BeginCustomDraw(IggyCustomDrawCallbackRegion *region, float matrix[16]); /* Call at the beginning of Iggy custom draw callback to clear any odd render states GDraw has set, and to get the current 2D object-to-world transformation. */ IDOC extern void RADLINK gdraw_psp2_EndCustomDraw(IggyCustomDrawCallbackRegion *region); /* Call at the end of Iggy custom draw callback so GDraw can restore its render states. */ IDOC extern GDrawTexture *gdraw_psp2_WrappedTextureCreate(SceGxmTexture *tex); /* Create a wrapped texture from a GXM texture. A wrapped texture can be used to let Iggy draw using the contents of a texture you create and manage on your own. For example, you might render to this texture, or stream video into it. Wrapped textures take up a handle. They will never be freed or otherwise modified by GDraw; nor will GDraw change any reference counts. All this is up to the application. GDraw makes a copy of the contents of the sceGxmTexture (the contents of the struct that is, not the data it points to). If you later modify the fields of "tex", you need to call $gdraw_psp2_WrappedTextureChange. */ IDOC extern void gdraw_psp2_WrappedTextureChange(GDrawTexture *handle, SceGxmTexture *tex); /* Switch an existing GDrawTexture * that represents a wrapped texture to use a new underlying GXM texture. For example, you might internally double-buffer a dynamically updated texture. As above, GDraw will leave this texture alone and not touch any reference counts. */ IDOC extern void gdraw_psp2_WrappedTextureDestroy(GDrawTexture *handle); /* Destroys the GDraw wrapper for a wrapped texture object. This will free up a GDraw texture handle but not release the associated GXM texture; that is up to you. */ IDOC extern GDrawTexture * RADLINK gdraw_psp2_MakeTextureFromResource(U8 *file_in_memory, S32 length, IggyFileTexturePSP2 *tex); /* Sets up a texture loaded from a .psp2.iggytex file. */ extern void RADLINK gdraw_psp2_DestroyTextureFromResource(GDrawTexture *tex);