mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-18 12:05:14 -04:00
Tidy up readme
This commit is contained in:
parent
e01ab2878d
commit
9a1072d45a
115
readme.md
115
readme.md
@ -3,7 +3,59 @@ ClassiCube is a custom Minecraft Classic compatible client written in C from scr
|
||||
|
||||

|
||||
|
||||
### Supported systems
|
||||
# Information
|
||||
|
||||
ClassiCube aims to replicate the 2009 Minecraft Classic client while also:
|
||||
* offering **optional** additions to improve gameplay
|
||||
* providing much better performance for a large range of hardware
|
||||
* running on many different systems (desktop, web, mobile, consoles)
|
||||
|
||||
ClassiCube is not trying to replicate modern Minecraft versions - and therefore it doesn't (and won't) support:
|
||||
* survival mode
|
||||
* logging in with Microsoft or Mojang accounts
|
||||
* connecting to Minecraft Java Edition or Bedrock Edition servers
|
||||
<br/>
|
||||
|
||||
You can download ClassiCube [from here](https://www.classicube.net/download/) and the very latest builds [from here](https://www.classicube.net/nightlies/).
|
||||
|
||||
### We need your help
|
||||
|
||||
ClassiCube strives to replicate the original Minecraft Classic experience by **strictly following a [clean room](https://en.wikipedia.org/wiki/Clean_room_design) reverse engineering approach**.
|
||||
|
||||
If you're interested in documenting or verifying the behaviour of the original Minecraft Classic, please get in contact with me. (`unknownshadow200` on Discord)
|
||||
|
||||
### What ClassiCube is
|
||||
* A complete re-implementation of Minecraft Classic 0.30, with **optional** additions
|
||||
* Partially supports some features of Minecraft Classic versions before 0.30
|
||||
* Lightweight, minimal memory usage compared to original Minecraft Classic
|
||||
* Much better performance than original Minecraft Classic
|
||||
* Works with effectively all graphics cards that support OpenGL or Direct3D 9
|
||||
* Runs on Windows, macOS, Linux, Android, iOS, and in a web browser
|
||||
* Also runs on OpenBSD, FreeBSD, NetBSD, Solaris, Haiku, IRIX, SerenityOS
|
||||
* Although still work in progresses, also runs on various consoles
|
||||
|
||||
### Instructions
|
||||
Initially, you will need to run ClassiCube.exe to download the required assets from minecraft.net and classicube.net.<br>
|
||||
Just click 'OK' to the dialog menu that appears when you start the launcher.
|
||||
|
||||
**Singleplayer**
|
||||
Run ClassiCube.exe, then click Singleplayer at the main menu.
|
||||
|
||||
**Multiplayer**
|
||||
Run ClassiCube.exe. You can connect to LAN/locally hosted servers, and classicube.net servers if you have a [ClassiCube account](https://www.classicube.net/).
|
||||
|
||||
<br/>
|
||||
|
||||
**Note:** When running from within VirtualBox, disable Mouse Integration, otherwise the camera will not work properly
|
||||
|
||||
#### *Stuck on OpenGL 1.1?*
|
||||
The most common reason for being stuck on OpenGL 1.1 is non-working GPU drivers - so if possible, you should try either installing or updating the drivers for your GPU.
|
||||
|
||||
Otherwise:
|
||||
* On Windows, you can still run the OpenGL build of ClassiCube anyways. (You can try downloading and using the MESA software renderer from [here](http://download.qt.io/development_releases/prebuilt/llvmpipe/windows/) for slightly better performance though)
|
||||
* On other operating systems, you will have to [compile the game yourself](#Compiling). Don't forget to add `-DCC_BUILD_GL11` to the compilation command line so that the compiled game supports OpenGL 1.1.
|
||||
|
||||
## Supported systems
|
||||
|
||||
ClassiCube runs on:
|
||||
* Windows - 95 and later
|
||||
@ -25,59 +77,14 @@ ClassiCube runs on:
|
||||
<li> BeOS - untested on actual hardware </li>
|
||||
<li> IRIX - needs <code>curl</code> and <code>openal</code> packages </li>
|
||||
<li> SerenityOS - needs <code>SDL2</code> </li>
|
||||
<li> 3DS - unfinished, but <a href="https://github.com/UnknownShadow200/ClassiCube/actions/workflows/build_3ds.yml">usable</a> </li>
|
||||
<li> Wii - unfinished, but <a href="https://github.com/UnknownShadow200/ClassiCube/actions/workflows/build_wiigc.yml">usable</a> </li>
|
||||
<li> GameCube - unfinished, but <a href="https://github.com/UnknownShadow200/ClassiCube/actions/workflows/build_wiigc.yml">usable</a> </li>
|
||||
<li> PSP - unfinished, rendering issues </li>
|
||||
<li> Dreamcast - unfinished, but renders </li>
|
||||
<li> PS Vita - majorly unfinished </li>
|
||||
<li> Xbox - majorly unfinished </li>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
You can download ClassiCube [from here](https://www.classicube.net/download/) and the very latest builds [from here](https://www.classicube.net/nightlies/).
|
||||
|
||||
### We need your help
|
||||
|
||||
ClassiCube strives to support providing an experience identical to the original Minecraft Classic by **strictly following a [clean room](https://en.wikipedia.org/wiki/Clean_room_design) reverse engineering approach**.
|
||||
|
||||
If you're interested in documenting or verifying the behaviour of the original Minecraft Classic, please get in contact with me. (`unknownshadow200` on Discord)
|
||||
|
||||
## Information
|
||||
|
||||
#### What ClassiCube is
|
||||
* A complete re-implementation of Minecraft Classic 0.30, with **optional** additions
|
||||
* Partially supports some features of Minecraft Classic versions before 0.30
|
||||
* Lightweight, minimal memory usage compared to original Minecraft Classic
|
||||
* Much better performance than original Minecraft Classic
|
||||
* Works with effectively all graphics cards that support OpenGL or Direct3D 9
|
||||
* Runs on Windows, macOS, Linux, Android, iOS, and in a web browser
|
||||
* Also runs on OpenBSD, FreeBSD, NetBSD, Solaris, Haiku, IRIX, SerenityOS
|
||||
* Although still work in progresses, also runs on various consoles
|
||||
|
||||
#### What ClassiCube isn't
|
||||
* It does not work with Minecraft Java or Bedrock edition servers
|
||||
* It does not have a survival mode (nor will such a mode be added)
|
||||
* It does not support logging in with Mojang/Minecraft accounts
|
||||
|
||||
**Note:** When running from within VirtualBox, disable Mouse Integration, otherwise the camera will not work properly
|
||||
|
||||
#### Instructions
|
||||
Initially, you will need to run ClassiCube.exe to download the required assets from minecraft.net and classicube.net.<br>
|
||||
Just click 'OK' to the dialog menu that appears when you start the launcher.
|
||||
|
||||
**Singleplayer**
|
||||
Run ClassiCube.exe, then click Singleplayer at the main menu.
|
||||
|
||||
**Multiplayer**
|
||||
Run ClassiCube.exe. You can connect to LAN/locally hosted servers, and classicube.net servers if you have a [ClassiCube account](https://www.classicube.net/).
|
||||
|
||||
##### *Stuck on OpenGL 1.1?*
|
||||
The most common reason for being stuck on OpenGL 1.1 is non-working GPU drivers - so if possible, you should try either installing or updating the drivers for your GPU.
|
||||
|
||||
Otherwise:
|
||||
* On Windows, you can still run the OpenGL build of ClassiCube anyways. (You can try downloading and using the MESA software renderer from [here](http://download.qt.io/development_releases/prebuilt/llvmpipe/windows/) for slightly better performance though)
|
||||
* On other operating systems, you will have to [compile the game yourself](#Compiling). Don't forget to add `-DCC_BUILD_GL11` to the compilation command line so that the compiled game supports OpenGL 1.1.
|
||||
<li> 3DS - <a href="https://github.com/UnknownShadow200/ClassiCube/actions/workflows/build_3ds.yml">unfinished, but usable</a> </li>
|
||||
<li> Wii - <a href="https://github.com/UnknownShadow200/ClassiCube/actions/workflows/build_wiigc.yml">unfinished, but usable</a> </li>
|
||||
<li> GameCube - <a href="https://github.com/UnknownShadow200/ClassiCube/actions/workflows/build_wiigc.yml">unfinished, but usable</a> </li>
|
||||
<li> Dreamcast - <a href="https://github.com/UnknownShadow200/ClassiCube/actions/workflows/build_dreamcast.yml">unfinished, but renders</a> </li>
|
||||
<li> PSP - <a href="https://github.com/UnknownShadow200/ClassiCube/actions/workflows/build_psp.yml">unfinished, rendering issues</a> </li>
|
||||
<li> PS Vita - <a href="https://github.com/UnknownShadow200/ClassiCube/actions/workflows/build_vita.yml">unfinished, rendering issues</a> </li>
|
||||
<li> Xbox -<a href="https://github.com/UnknownShadow200/ClassiCube/actions/workflows/build_xbox"> unfinished, major rendering issues</a> </li>
|
||||
<li> PS3 - doesn't render properly yet </li>
|
||||
|
||||
# Compiling
|
||||
|
||||
|
@ -4,8 +4,7 @@
|
||||
#include "Errors.h"
|
||||
#include "Logger.h"
|
||||
#include "Window.h"
|
||||
#include "GL/gl.h"
|
||||
#include "GL/glkos.h"
|
||||
#include "../third_party/gldc/include/gldc.h"
|
||||
#include <malloc.h>
|
||||
#include <kos.h>
|
||||
#include <dc/matrix.h>
|
||||
|
25
third_party/gldc/LICENSE
vendored
Normal file
25
third_party/gldc/LICENSE
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2018, Luke Benstead
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
28
third_party/gldc/Makefile
vendored
Normal file
28
third_party/gldc/Makefile
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
SOURCE_DIRS := src src/yalloc
|
||||
|
||||
C_FILES := $(foreach dir,$(SOURCE_DIRS),$(wildcard $(dir)/*.c))
|
||||
OBJS := $(notdir $(C_FILES:%.c=%.o))
|
||||
|
||||
C_FLAGS = -O3 -DNDEBUG -mfsrra -mfsca -ffp-contract=fast -ffast-math -O3 -mpretend-cmove -fexpensive-optimizations -fomit-frame-pointer -finline-functions -flto -fno-fat-lto-objects -ml -m4-single-only -ffunction-sections -fdata-sections -std=gnu99
|
||||
|
||||
C_DEFINES = -DDREAMCAST -DNDEBUG -D__DREAMCAST__ -D__arch_dreamcast -D_arch_dreamcast -D_arch_sub_pristine
|
||||
|
||||
C_INCLUDES = -I/opt/toolchains/dc/kos/include -I/opt/toolchains/dc/kos/kernel/arch/dreamcast/include -I/opt/toolchains/dc/kos/addons/include
|
||||
|
||||
TARGET := libGLdc.a
|
||||
|
||||
ifeq ($(strip $(KOS_BASE)),)
|
||||
$(error "Please set KOS variables in your environment.")
|
||||
endif
|
||||
|
||||
default: $(TARGET)
|
||||
|
||||
%.o: src/%.c
|
||||
kos-cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -c $< -o $@
|
||||
|
||||
%.o: src/yalloc/%.c
|
||||
kos-cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -c $< -o $@
|
||||
|
||||
$(TARGET): $(OBJS)
|
||||
kos-ar cr $@ $<
|
||||
kos-ranlib $@
|
66
third_party/gldc/README.md
vendored
Normal file
66
third_party/gldc/README.md
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
This is a fork of GLdc optimised for the Dreamcast port of ClassiCube, and unfortunately is essentially useless for any other project
|
||||
|
||||
---
|
||||
|
||||
# GLdc
|
||||
|
||||
**Development of GLdc has moved to [Gitlab](https://gitlab.com/simulant/GLdc)**
|
||||
|
||||
This is a partial implementation of OpenGL 1.2 for the SEGA Dreamcast for use
|
||||
with the KallistiOS SDK.
|
||||
|
||||
It began as a fork of libGL by Josh Pearson but has undergone a large refactor
|
||||
which is essentially a rewrite.
|
||||
|
||||
The aim is to implement as much of OpenGL 1.2 as possible, and to add additional
|
||||
features via extensions.
|
||||
|
||||
Things left to (re)implement:
|
||||
|
||||
- Spotlights (Trivial)
|
||||
- Framebuffer extension (Trivial)
|
||||
- Texture Matrix (Trivial)
|
||||
|
||||
Things I'd like to do:
|
||||
|
||||
- Use a clean "gl.h"
|
||||
- Define an extension for modifier volumes
|
||||
- Add support for point sprites
|
||||
- Optimise, add unit tests for correctness
|
||||
|
||||
# Compiling
|
||||
|
||||
GLdc uses CMake for its build system, it currently ships with two "backends":
|
||||
|
||||
- kospvr - This is the hardware-accelerated Dreamcast backend
|
||||
- software - This is a stub software rasterizer used for testing testing and debugging
|
||||
|
||||
To compile a Dreamcast debug build, you'll want to do something like the following:
|
||||
|
||||
```
|
||||
mkdir dcbuild
|
||||
cd dcbuild
|
||||
cmake -DCMAKE_TOOLCHAIN_FILE=../toolchains/Dreamcast.cmake -G "Unix Makefiles" ..
|
||||
make
|
||||
```
|
||||
|
||||
For a release build, replace the cmake line with with the following:
|
||||
```
|
||||
cmake -DCMAKE_TOOLCHAIN_FILE=../toolchains/Dreamcast.cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release ..
|
||||
```
|
||||
|
||||
You will need KallistiOS compiled and configured (e.g. the KOS_BASE environment
|
||||
variable must be set)
|
||||
|
||||
To compile for PC:
|
||||
|
||||
```
|
||||
mkdir pcbuild
|
||||
cd pcbuild
|
||||
cmake -G "Unix Makefiles" ..
|
||||
make
|
||||
```
|
||||
|
||||
# Special Thanks!
|
||||
|
||||
- Massive shout out to Hayden Kowalchuk for diagnosing and fixing a large number of bugs while porting GL Quake to the Dreamcast. Absolute hero!
|
348
third_party/gldc/include/gldc.h
vendored
Normal file
348
third_party/gldc/include/gldc.h
vendored
Normal file
@ -0,0 +1,348 @@
|
||||
/* KallistiGL for KallistiOS ##version##
|
||||
|
||||
libgl/gl.h
|
||||
Copyright (C) 2013-2014 Josh "PH3NOM" Pearson
|
||||
Copyright (C) 2014, 2016 Lawrence Sebald
|
||||
|
||||
Some functionality adapted from the original KOS libgl:
|
||||
Copyright (C) 2001 Dan Potter
|
||||
Copyright (C) 2002 Benoit Miller
|
||||
|
||||
This API implements much but not all of the OpenGL 1.1 for KallistiOS.
|
||||
*/
|
||||
|
||||
#ifndef __GL_GL_H
|
||||
#define __GL_GL_H
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__BEGIN_DECLS
|
||||
|
||||
#include <math.h>
|
||||
|
||||
/* Primitive Types taken from GL for compatability */
|
||||
/* Not all types are implemented in Open GL DC V.1.0 */
|
||||
#define GL_POINTS 0x0000
|
||||
#define GL_LINES 0x0001
|
||||
#define GL_TRIANGLES 0x0004
|
||||
#define GL_TRIANGLE_STRIP 0x0005
|
||||
#define GL_QUADS 0x0007
|
||||
|
||||
/* FrontFaceDirection */
|
||||
#define GL_CW 0x0900
|
||||
#define GL_CCW 0x0901
|
||||
|
||||
#define GL_NONE 0
|
||||
#define GL_FRONT_LEFT 0x0400
|
||||
#define GL_FRONT_RIGHT 0x0401
|
||||
#define GL_BACK_LEFT 0x0402
|
||||
#define GL_BACK_RIGHT 0x0403
|
||||
#define GL_FRONT 0x0404
|
||||
#define GL_BACK 0x0405
|
||||
#define GL_LEFT 0x0406
|
||||
#define GL_RIGHT 0x0407
|
||||
#define GL_FRONT_AND_BACK 0x0408
|
||||
#define GL_CULL_FACE 0x0B44
|
||||
#define GL_CULL_FACE_MODE 0x0B45
|
||||
#define GL_FRONT_FACE 0x0B46
|
||||
|
||||
/* Scissor box */
|
||||
#define GL_SCISSOR_TEST 0x0008 /* capability bit */
|
||||
#define GL_SCISSOR_BOX 0x0C10
|
||||
|
||||
/* Depth buffer */
|
||||
#define GL_NEVER 0x0200
|
||||
#define GL_LESS 0x0201
|
||||
#define GL_EQUAL 0x0202
|
||||
#define GL_LEQUAL 0x0203
|
||||
#define GL_GREATER 0x0204
|
||||
#define GL_NOTEQUAL 0x0205
|
||||
#define GL_GEQUAL 0x0206
|
||||
#define GL_ALWAYS 0x0207
|
||||
#define GL_DEPTH_TEST 0x0B71
|
||||
#define GL_DEPTH_BITS 0x0D56
|
||||
#define GL_DEPTH_FUNC 0x0B74
|
||||
#define GL_DEPTH_WRITEMASK 0x0B72
|
||||
#define GL_DEPTH_COMPONENT 0x1902
|
||||
|
||||
/* Blending: Simply Need to Map GL constants to PVR constants */
|
||||
#define GL_BLEND_DST 0x0BE0
|
||||
#define GL_BLEND_SRC 0x0BE1
|
||||
#define GL_BLEND 0x0BE2 /* capability bit */
|
||||
|
||||
#define GL_ZERO 0x0
|
||||
#define GL_ONE 0x1
|
||||
#define GL_SRC_COLOR 0x0300
|
||||
#define GL_ONE_MINUS_SRC_COLOR 0x0301
|
||||
#define GL_SRC_ALPHA 0x0302
|
||||
#define GL_ONE_MINUS_SRC_ALPHA 0x0303
|
||||
#define GL_DST_ALPHA 0x0304
|
||||
#define GL_ONE_MINUS_DST_ALPHA 0x0305
|
||||
#define GL_DST_COLOR 0x0306
|
||||
#define GL_ONE_MINUS_DST_COLOR 0x0307
|
||||
#define GL_SRC_ALPHA_SATURATE 0x0308
|
||||
|
||||
/* Misc texture constants */
|
||||
#define GL_TEXTURE_2D 0x0001 /* capability bit */
|
||||
|
||||
/* Texture Environment */
|
||||
#define GL_TEXTURE_ENV_MODE 0x2200
|
||||
#define GL_REPLACE 0x1E01
|
||||
#define GL_MODULATE 0x2100
|
||||
#define GL_DECAL 0x2101
|
||||
|
||||
/* TextureMagFilter */
|
||||
#define GL_NEAREST 0x2600
|
||||
#define GL_LINEAR 0x2601
|
||||
|
||||
/* Fog */
|
||||
#define GL_FOG 0x0004 /* capability bit */
|
||||
|
||||
/* Client state caps */
|
||||
#define GL_VERTEX_ARRAY 0x8074
|
||||
#define GL_COLOR_ARRAY 0x8076
|
||||
#define GL_TEXTURE_COORD_ARRAY 0x8078
|
||||
|
||||
#define GL_FRONT_AND_BACK 0x0408
|
||||
#define GL_FRONT 0x0404
|
||||
#define GL_BACK 0x0405
|
||||
|
||||
#define GL_SHADE_MODEL 0x0b54
|
||||
#define GL_FLAT 0x1d00
|
||||
#define GL_SMOOTH 0x1d01
|
||||
|
||||
/* Data types */
|
||||
#define GL_BYTE 0x1400
|
||||
#define GL_UNSIGNED_BYTE 0x1401
|
||||
#define GL_SHORT 0x1402
|
||||
#define GL_UNSIGNED_SHORT 0x1403
|
||||
#define GL_INT 0x1404
|
||||
#define GL_UNSIGNED_INT 0x1405
|
||||
#define GL_FLOAT 0x1406
|
||||
|
||||
/* ErrorCode */
|
||||
#define GL_NO_ERROR 0
|
||||
#define GL_INVALID_ENUM 0x0500
|
||||
#define GL_INVALID_VALUE 0x0501
|
||||
#define GL_INVALID_OPERATION 0x0502
|
||||
#define GL_OUT_OF_MEMORY 0x0505
|
||||
|
||||
/* GetPName */
|
||||
#define GL_MAX_TEXTURE_SIZE 0x0D33
|
||||
|
||||
/* StringName */
|
||||
#define GL_VENDOR 0x1F00
|
||||
#define GL_RENDERER 0x1F01
|
||||
|
||||
#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033
|
||||
#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
|
||||
#define GL_UNSIGNED_SHORT_5_6_5 0x8363
|
||||
#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364
|
||||
#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365
|
||||
#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366
|
||||
|
||||
#define GL_RGBA 0x1908
|
||||
|
||||
/* Polygons */
|
||||
#define GL_POINT 0x1B00
|
||||
#define GL_LINE 0x1B01
|
||||
#define GL_FILL 0x1B02
|
||||
#define GL_CW 0x0900
|
||||
#define GL_CCW 0x0901
|
||||
#define GL_FRONT 0x0404
|
||||
#define GL_BACK 0x0405
|
||||
#define GL_POLYGON_MODE 0x0B40
|
||||
#define GL_POLYGON_SMOOTH 0x0B41
|
||||
#define GL_POLYGON_STIPPLE 0x0B42
|
||||
#define GL_EDGE_FLAG 0x0B43
|
||||
#define GL_CULL_FACE 0x0B44
|
||||
#define GL_CULL_FACE_MODE 0x0B45
|
||||
#define GL_FRONT_FACE 0x0B46
|
||||
|
||||
#define GLbyte char
|
||||
#define GLshort short
|
||||
#define GLint int
|
||||
#define GLfloat float
|
||||
#define GLvoid void
|
||||
#define GLushort unsigned short
|
||||
#define GLuint unsigned int
|
||||
#define GLenum unsigned int
|
||||
#define GLsizei unsigned int
|
||||
#define GLclampf float
|
||||
#define GLclampd float
|
||||
#define GLubyte unsigned char
|
||||
#define GLboolean unsigned char
|
||||
#define GL_FALSE 0
|
||||
#define GL_TRUE 1
|
||||
|
||||
/* Stubs for portability */
|
||||
#define GL_ALPHA_TEST 0x0BC0
|
||||
|
||||
#define GLAPI extern
|
||||
#define APIENTRY
|
||||
|
||||
/* Start Submission of Primitive Data */
|
||||
/* Currently Supported Primitive Types:
|
||||
-GL_POINTS ( does NOT work with glDrawArrays )( ZClipping NOT supported )
|
||||
-GL_TRIANGLES ( works with glDrawArrays )( ZClipping supported )
|
||||
-GL_TRIANLGLE_STRIP ( works with glDrawArrays )( ZClipping supported )
|
||||
-GL_QUADS ( works with glDrawArrays )( ZClipping supported )
|
||||
**/
|
||||
|
||||
/* Enable / Disable Capability */
|
||||
/* Currently Supported Capabilities:
|
||||
GL_TEXTURE_2D
|
||||
GL_BLEND
|
||||
GL_DEPTH_TEST
|
||||
GL_LIGHTING
|
||||
GL_SCISSOR_TEST
|
||||
GL_FOG
|
||||
GL_CULL_FACE
|
||||
GL_KOS_NEARZ_CLIPPING
|
||||
GL_KOS_TEXTURE_MATRIX
|
||||
*/
|
||||
GLAPI void glEnable(GLenum cap);
|
||||
GLAPI void glDisable(GLenum cap);
|
||||
|
||||
/* Clear Caps */
|
||||
GLAPI void glClear(GLuint mode);
|
||||
GLAPI void glClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a);
|
||||
|
||||
/* Depth Testing */
|
||||
GLAPI void glClearDepth(GLfloat depth);
|
||||
GLAPI void glClearDepthf(GLfloat depth);
|
||||
GLAPI void glDepthMask(GLboolean flag);
|
||||
GLAPI void glDepthFunc(GLenum func);
|
||||
GLAPI void glDepthRange(GLclampf n, GLclampf f);
|
||||
GLAPI void glDepthRangef(GLclampf n, GLclampf f);
|
||||
|
||||
/* Culling */
|
||||
GLAPI void glFrontFace(GLenum mode);
|
||||
GLAPI void glCullFace(GLenum mode);
|
||||
|
||||
/* Shading - Flat or Goraud */
|
||||
GLAPI void glShadeModel(GLenum mode);
|
||||
|
||||
/* Blending */
|
||||
GLAPI void glBlendFunc(GLenum sfactor, GLenum dfactor);
|
||||
|
||||
GLAPI GLuint gldcGenTexture(void);
|
||||
GLAPI void gldcDeleteTexture(GLuint texture);
|
||||
GLAPI void gldcBindTexture(GLuint texture);
|
||||
|
||||
/* Loads texture from SH4 RAM into PVR VRAM applying color conversion if needed */
|
||||
/* internalformat must be one of the following constants:
|
||||
GL_RGB
|
||||
GL_RGBA
|
||||
|
||||
format must be the same as internalformat
|
||||
|
||||
if internal format is GL_RGBA, type must be one of the following constants:
|
||||
GL_BYTE
|
||||
GL_UNSIGNED_BYTE
|
||||
GL_SHORT
|
||||
GL_UNSIGNED_SHORT
|
||||
GL_FLOAT
|
||||
GL_UNSIGNED_SHORT_4_4_4_4
|
||||
GL_UNSIGNED_SHORT_4_4_4_4_TWID
|
||||
GL_UNSIGNED_SHORT_1_5_5_5
|
||||
GL_UNSIGNED_SHORT_1_5_5_5_TWID
|
||||
*/
|
||||
GLAPI void gldcAllocTexture(GLsizei w, GLsizei h, GLenum format, GLenum type);
|
||||
GLAPI void gldcGetTexture(GLvoid** data, GLsizei* width, GLsizei* height);
|
||||
|
||||
|
||||
/* GL Array API - Only GL_TRIANGLES, GL_TRIANGLE_STRIP, and GL_QUADS are supported */
|
||||
GLAPI void glVertexPointer(GLint size, GLenum type,
|
||||
GLsizei stride, const GLvoid *pointer);
|
||||
|
||||
/* Array Data Submission */
|
||||
GLAPI void glDrawArrays(GLenum mode, GLint first, GLsizei count);
|
||||
GLAPI void glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);
|
||||
|
||||
GLAPI void glEnableClientState(GLenum cap);
|
||||
GLAPI void glDisableClientState(GLenum cap);
|
||||
|
||||
/* Transformation / Matrix Functions */
|
||||
|
||||
GLAPI void glViewport(GLint x, GLint y, GLsizei width, GLsizei height);
|
||||
|
||||
GLAPI void glScissor(GLint x, GLint y, GLsizei width, GLsizei height);
|
||||
|
||||
/* glGet Functions */
|
||||
GLAPI void glGetIntegerv(GLenum pname, GLint *params);
|
||||
GLAPI const GLubyte* glGetString(GLenum name);
|
||||
|
||||
/* Error handling */
|
||||
GLAPI GLenum glGetError(void);
|
||||
|
||||
|
||||
GLAPI void glAlphaFunc(GLenum func, GLclampf ref);
|
||||
/* Non Operational Stubs for portability */
|
||||
GLAPI void glPolygonOffset(GLfloat factor, GLfloat units);
|
||||
GLAPI void glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha);
|
||||
|
||||
|
||||
/* ATI_meminfo extension */
|
||||
#define GL_VBO_FREE_MEMORY_ATI 0x87FB
|
||||
#define GL_TEXTURE_FREE_MEMORY_ATI 0x87FC
|
||||
#define GL_RENDERBUFFER_FREE_MEMORY_ATI 0x87FD
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Dreamcast specific compressed + twiddled formats.
|
||||
* We use constants from the range 0xEEE0 onwards
|
||||
* to avoid trampling any real GL constants (this is in the middle of the
|
||||
* any_vendor_future_use range defined in the GL enum.spec file.
|
||||
*/
|
||||
#define GL_UNSIGNED_SHORT_5_6_5_TWID_KOS 0xEEE0
|
||||
#define GL_UNSIGNED_SHORT_1_5_5_5_REV_TWID_KOS 0xEEE2
|
||||
#define GL_UNSIGNED_SHORT_4_4_4_4_REV_TWID_KOS 0xEEE3
|
||||
|
||||
#define GL_NEARZ_CLIPPING_KOS 0xEEFA
|
||||
|
||||
|
||||
/* Initialize the GL pipeline. GL will initialize the PVR. */
|
||||
GLAPI void glKosInit();
|
||||
|
||||
typedef struct {
|
||||
/* If GL_TRUE, enables pvr autosorting, this *will* break glDepthFunc/glDepthTest */
|
||||
GLboolean autosort_enabled;
|
||||
|
||||
/* If GL_TRUE, enables the PVR FSAA */
|
||||
GLboolean fsaa_enabled;
|
||||
|
||||
/* Initial capacity of each of the OP, TR and PT lists in vertices */
|
||||
GLuint initial_op_capacity;
|
||||
GLuint initial_tr_capacity;
|
||||
GLuint initial_pt_capacity;
|
||||
|
||||
} GLdcConfig;
|
||||
|
||||
|
||||
GLAPI void glKosInitConfig(GLdcConfig* config);
|
||||
|
||||
/* Usage:
|
||||
*
|
||||
* GLdcConfig config;
|
||||
* glKosInitConfig(&config);
|
||||
*
|
||||
* config.autosort_enabled = GL_TRUE;
|
||||
*
|
||||
* glKosInitEx(&config);
|
||||
*/
|
||||
GLAPI void glKosInitEx(GLdcConfig* config);
|
||||
GLAPI void glKosSwapBuffers();
|
||||
\
|
||||
|
||||
/* Memory allocation extension (GL_KOS_texture_memory_management) */
|
||||
GLAPI GLvoid glDefragmentTextureMemory_KOS(void);
|
||||
|
||||
#define GL_FREE_TEXTURE_MEMORY_KOS 0xEF3D
|
||||
#define GL_USED_TEXTURE_MEMORY_KOS 0xEF3E
|
||||
#define GL_FREE_CONTIGUOUS_TEXTURE_MEMORY_KOS 0xEF3F
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif /* !__GL_GL_H */
|
55
third_party/gldc/src/aligned_vector.c
vendored
Normal file
55
third_party/gldc/src/aligned_vector.c
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "private.h"
|
||||
#include "aligned_vector.h"
|
||||
|
||||
extern inline void* aligned_vector_resize(AlignedVector* vector, const uint32_t element_count);
|
||||
extern inline void* aligned_vector_extend(AlignedVector* vector, const uint32_t additional_count);
|
||||
extern inline void* aligned_vector_reserve(AlignedVector* vector, uint32_t element_count);
|
||||
extern inline void* aligned_vector_push_back(AlignedVector* vector, const void* objs, uint32_t count);
|
||||
|
||||
void aligned_vector_init(AlignedVector* vector, uint32_t element_size) {
|
||||
/* Now initialize the header*/
|
||||
AlignedVectorHeader* const hdr = &vector->hdr;
|
||||
hdr->size = 0;
|
||||
hdr->capacity = ALIGNED_VECTOR_CHUNK_SIZE;
|
||||
hdr->element_size = element_size;
|
||||
vector->data = NULL;
|
||||
|
||||
/* Reserve some initial capacity. This will do the allocation but not set up the header */
|
||||
void* ptr = aligned_vector_reserve(vector, ALIGNED_VECTOR_CHUNK_SIZE);
|
||||
assert(ptr);
|
||||
(void) ptr;
|
||||
}
|
||||
|
||||
void aligned_vector_shrink_to_fit(AlignedVector* vector) {
|
||||
AlignedVectorHeader* const hdr = &vector->hdr;
|
||||
if(hdr->size == 0) {
|
||||
uint32_t element_size = hdr->element_size;
|
||||
free(vector->data);
|
||||
|
||||
/* Reallocate the header */
|
||||
vector->data = NULL;
|
||||
hdr->size = hdr->capacity = 0;
|
||||
hdr->element_size = element_size;
|
||||
} else {
|
||||
uint32_t new_byte_size = (hdr->size * hdr->element_size);
|
||||
uint8_t* original_data = vector->data;
|
||||
vector->data = (unsigned char*) memalign(0x20, new_byte_size);
|
||||
|
||||
if(original_data) {
|
||||
FASTCPY(vector->data, original_data, new_byte_size);
|
||||
free(original_data);
|
||||
}
|
||||
hdr->capacity = hdr->size;
|
||||
}
|
||||
}
|
||||
|
||||
void aligned_vector_cleanup(AlignedVector* vector) {
|
||||
aligned_vector_clear(vector);
|
||||
aligned_vector_shrink_to_fit(vector);
|
||||
}
|
217
third_party/gldc/src/aligned_vector.h
vendored
Normal file
217
third_party/gldc/src/aligned_vector.h
vendored
Normal file
@ -0,0 +1,217 @@
|
||||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) || defined(__WIN32__)
|
||||
/* Linux + Kos define this, OSX does not, so just use malloc there */
|
||||
static inline void* memalign(size_t alignment, size_t size) {
|
||||
(void) alignment;
|
||||
return malloc(size);
|
||||
}
|
||||
#else
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define AV_FORCE_INLINE static inline
|
||||
#else
|
||||
#define AV_NO_INSTRUMENT inline __attribute__((no_instrument_function))
|
||||
#define AV_INLINE_DEBUG AV_NO_INSTRUMENT __attribute__((always_inline))
|
||||
#define AV_FORCE_INLINE static AV_INLINE_DEBUG
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __DREAMCAST__
|
||||
#include <kos/string.h>
|
||||
|
||||
AV_FORCE_INLINE void *AV_MEMCPY4(void *dest, const void *src, size_t len)
|
||||
{
|
||||
if(!len)
|
||||
{
|
||||
return dest;
|
||||
}
|
||||
|
||||
const uint8_t *s = (uint8_t *)src;
|
||||
uint8_t *d = (uint8_t *)dest;
|
||||
|
||||
uint32_t diff = (uint32_t)d - (uint32_t)(s + 1); // extra offset because input gets incremented before output is calculated
|
||||
// Underflow would be like adding a negative offset
|
||||
|
||||
// Can use 'd' as a scratch reg now
|
||||
asm volatile (
|
||||
"clrs\n" // Align for parallelism (CO) - SH4a use "stc SR, Rn" instead with a dummy Rn
|
||||
".align 2\n"
|
||||
"0:\n\t"
|
||||
"dt %[size]\n\t" // (--len) ? 0 -> T : 1 -> T (EX 1)
|
||||
"mov.b @%[in]+, %[scratch]\n\t" // scratch = *(s++) (LS 1/2)
|
||||
"bf.s 0b\n\t" // while(s != nexts) aka while(!T) (BR 1/2)
|
||||
" mov.b %[scratch], @(%[offset], %[in])\n" // *(datatype_of_s*) ((char*)s + diff) = scratch, where src + diff = dest (LS 1)
|
||||
: [in] "+&r" ((uint32_t)s), [scratch] "=&r" ((uint32_t)d), [size] "+&r" (len) // outputs
|
||||
: [offset] "z" (diff) // inputs
|
||||
: "t", "memory" // clobbers
|
||||
);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
#else
|
||||
#define AV_MEMCPY4 memcpy
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint32_t size;
|
||||
uint32_t capacity;
|
||||
uint32_t element_size;
|
||||
} __attribute__((aligned(32))) AlignedVectorHeader;
|
||||
|
||||
typedef struct {
|
||||
AlignedVectorHeader hdr;
|
||||
uint8_t* data;
|
||||
} AlignedVector;
|
||||
|
||||
#define ALIGNED_VECTOR_CHUNK_SIZE 256u
|
||||
|
||||
|
||||
#define ROUND_TO_CHUNK_SIZE(v) \
|
||||
((((v) + ALIGNED_VECTOR_CHUNK_SIZE - 1) / ALIGNED_VECTOR_CHUNK_SIZE) * ALIGNED_VECTOR_CHUNK_SIZE)
|
||||
|
||||
|
||||
void aligned_vector_init(AlignedVector* vector, uint32_t element_size);
|
||||
|
||||
AV_FORCE_INLINE void* aligned_vector_at(const AlignedVector* vector, const uint32_t index) {
|
||||
const AlignedVectorHeader* hdr = &vector->hdr;
|
||||
assert(index < hdr->size);
|
||||
return vector->data + (index * hdr->element_size);
|
||||
}
|
||||
|
||||
AV_FORCE_INLINE void* aligned_vector_reserve(AlignedVector* vector, uint32_t element_count) {
|
||||
AlignedVectorHeader* hdr = &vector->hdr;
|
||||
|
||||
if(element_count < hdr->capacity) {
|
||||
return aligned_vector_at(vector, element_count);
|
||||
}
|
||||
|
||||
uint32_t original_byte_size = (hdr->size * hdr->element_size);
|
||||
|
||||
/* We overallocate so that we don't make small allocations during push backs */
|
||||
element_count = ROUND_TO_CHUNK_SIZE(element_count);
|
||||
|
||||
uint32_t new_byte_size = (element_count * hdr->element_size);
|
||||
uint8_t* original_data = vector->data;
|
||||
|
||||
vector->data = (uint8_t*) memalign(0x20, new_byte_size);
|
||||
assert(vector->data);
|
||||
|
||||
AV_MEMCPY4(vector->data, original_data, original_byte_size);
|
||||
free(original_data);
|
||||
|
||||
hdr->capacity = element_count;
|
||||
return vector->data + original_byte_size;
|
||||
}
|
||||
|
||||
AV_FORCE_INLINE AlignedVectorHeader* aligned_vector_header(const AlignedVector* vector) {
|
||||
return (AlignedVectorHeader*) &vector->hdr;
|
||||
}
|
||||
|
||||
AV_FORCE_INLINE uint32_t aligned_vector_size(const AlignedVector* vector) {
|
||||
const AlignedVectorHeader* hdr = &vector->hdr;
|
||||
return hdr->size;
|
||||
}
|
||||
|
||||
AV_FORCE_INLINE uint32_t aligned_vector_capacity(const AlignedVector* vector) {
|
||||
const AlignedVectorHeader* hdr = &vector->hdr;
|
||||
return hdr->capacity;
|
||||
}
|
||||
|
||||
AV_FORCE_INLINE void* aligned_vector_front(const AlignedVector* vector) {
|
||||
return vector->data;
|
||||
}
|
||||
|
||||
#define av_assert(x) \
|
||||
do {\
|
||||
if(!(x)) {\
|
||||
fprintf(stderr, "Assertion failed at %s:%d\n", __FILE__, __LINE__);\
|
||||
exit(1);\
|
||||
}\
|
||||
} while(0); \
|
||||
|
||||
/* Resizes the array and returns a pointer to the first new element (if upsizing) or NULL (if downsizing) */
|
||||
AV_FORCE_INLINE void* aligned_vector_resize(AlignedVector* vector, const uint32_t element_count) {
|
||||
void* ret = NULL;
|
||||
|
||||
AlignedVectorHeader* hdr = &vector->hdr;
|
||||
uint32_t previous_count = hdr->size;
|
||||
if(hdr->capacity <= element_count) {
|
||||
/* If we didn't have capacity, increase capacity (slow) */
|
||||
|
||||
aligned_vector_reserve(vector, element_count);
|
||||
hdr->size = element_count;
|
||||
|
||||
ret = aligned_vector_at(vector, previous_count);
|
||||
|
||||
av_assert(hdr->size == element_count);
|
||||
av_assert(hdr->size <= hdr->capacity);
|
||||
} else if(previous_count < element_count) {
|
||||
/* So we grew, but had the capacity, just get a pointer to
|
||||
* where we were */
|
||||
hdr->size = element_count;
|
||||
av_assert(hdr->size < hdr->capacity);
|
||||
ret = aligned_vector_at(vector, previous_count);
|
||||
} else if(hdr->size != element_count) {
|
||||
hdr->size = element_count;
|
||||
av_assert(hdr->size < hdr->capacity);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
AV_FORCE_INLINE void* aligned_vector_push_back(AlignedVector* vector, const void* objs, uint32_t count) {
|
||||
/* Resize enough room */
|
||||
AlignedVectorHeader* hdr = &vector->hdr;
|
||||
|
||||
assert(count);
|
||||
assert(hdr->element_size);
|
||||
|
||||
#ifndef NDEBUG
|
||||
uint32_t element_size = hdr->element_size;
|
||||
uint32_t initial_size = hdr->size;
|
||||
#endif
|
||||
|
||||
uint8_t* dest = (uint8_t*) aligned_vector_resize(vector, hdr->size + count);
|
||||
assert(dest);
|
||||
|
||||
/* Copy the objects in */
|
||||
AV_MEMCPY4(dest, objs, hdr->element_size * count);
|
||||
|
||||
assert(hdr->element_size == element_size);
|
||||
assert(hdr->size == initial_size + count);
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
||||
AV_FORCE_INLINE void* aligned_vector_extend(AlignedVector* vector, const uint32_t additional_count) {
|
||||
AlignedVectorHeader* hdr = &vector->hdr;
|
||||
void* ret = aligned_vector_resize(vector, hdr->size + additional_count);
|
||||
assert(ret); // Should always return something
|
||||
return ret;
|
||||
}
|
||||
|
||||
AV_FORCE_INLINE void aligned_vector_clear(AlignedVector* vector){
|
||||
AlignedVectorHeader* hdr = &vector->hdr;
|
||||
hdr->size = 0;
|
||||
}
|
||||
|
||||
void aligned_vector_shrink_to_fit(AlignedVector* vector);
|
||||
void aligned_vector_cleanup(AlignedVector* vector);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
374
third_party/gldc/src/draw.c
vendored
Normal file
374
third_party/gldc/src/draw.c
vendored
Normal file
@ -0,0 +1,374 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "private.h"
|
||||
#include "platform.h"
|
||||
|
||||
|
||||
static void* VERTEX_PTR;
|
||||
static GLsizei VERTEX_STRIDE;
|
||||
static GLuint ENABLED_VERTEX_ATTRIBUTES;
|
||||
|
||||
extern GLboolean AUTOSORT_ENABLED;
|
||||
|
||||
#define ITERATE(count) \
|
||||
GLuint i = count; \
|
||||
while(i--)
|
||||
|
||||
|
||||
void _glInitAttributePointers() {
|
||||
TRACE();
|
||||
VERTEX_PTR = NULL;
|
||||
VERTEX_STRIDE = 0;
|
||||
}
|
||||
|
||||
|
||||
GL_FORCE_INLINE PolyHeader *_glSubmissionTargetHeader(SubmissionTarget* target) {
|
||||
gl_assert(target->header_offset < aligned_vector_size(&target->output->vector));
|
||||
return aligned_vector_at(&target->output->vector, target->header_offset);
|
||||
}
|
||||
|
||||
GL_INLINE_DEBUG Vertex* _glSubmissionTargetStart(SubmissionTarget* target) {
|
||||
gl_assert(target->start_offset < aligned_vector_size(&target->output->vector));
|
||||
return aligned_vector_at(&target->output->vector, target->start_offset);
|
||||
}
|
||||
|
||||
Vertex* _glSubmissionTargetEnd(SubmissionTarget* target) {
|
||||
return _glSubmissionTargetStart(target) + target->count;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
float u, v;
|
||||
} Float2;
|
||||
|
||||
static const Float2 F2ZERO = {0.0f, 0.0f};
|
||||
|
||||
static void generateQuads(SubmissionTarget* target, const GLsizei first, const GLuint count) {
|
||||
/* Read from the client buffers and generate an array of ClipVertices */
|
||||
TRACE();
|
||||
|
||||
GLuint numQuads = count / 4;
|
||||
GLuint idx = first;
|
||||
|
||||
Vertex* start = _glSubmissionTargetStart(target);
|
||||
|
||||
const GLuint stride = VERTEX_STRIDE;
|
||||
|
||||
/* Copy the pos, uv and color directly in one go */
|
||||
const GLubyte* pos = VERTEX_PTR;
|
||||
const GLubyte* uv = (ENABLED_VERTEX_ATTRIBUTES & UV_ENABLED_FLAG) ? VERTEX_PTR : NULL;
|
||||
const GLubyte* col = VERTEX_PTR;
|
||||
|
||||
Vertex* dst = start;
|
||||
const float w = 1.0f;
|
||||
|
||||
// TODO: optimise
|
||||
for (GLuint i = 0; i < numQuads; ++i) {
|
||||
// 4 vertices per quad
|
||||
Vertex* it = dst;
|
||||
PREFETCH(it); // TODO: more prefetching?
|
||||
|
||||
for(GLuint j = 0; j < 4; ++j) {
|
||||
pos = (GLubyte*) VERTEX_PTR + (idx * stride);
|
||||
PREFETCH(pos);
|
||||
TransformVertex((const float*) pos, &w, it->xyz, &it->w);
|
||||
|
||||
col = pos + 12;
|
||||
*((uint32_t*) it->bgra) = *((uint32_t*) col);
|
||||
|
||||
if(uv) {
|
||||
uv = pos + 16;
|
||||
MEMCPY4(it->uv, uv, sizeof(float) * 2);
|
||||
} else {
|
||||
*((Float2*) it->uv) = F2ZERO;
|
||||
}
|
||||
|
||||
it++;
|
||||
idx++;
|
||||
}
|
||||
|
||||
// Quads [0, 1, 2, 3] -> Triangles [{0, 1, 2} {2, 3, 0}]
|
||||
PREFETCH(dst); // TODO: more prefetching?
|
||||
memcpy_vertex(dst + 5, dst + 0); dst[5].flags = GPU_CMD_VERTEX_EOL;
|
||||
memcpy_vertex(dst + 4, dst + 3); dst[4].flags = GPU_CMD_VERTEX;
|
||||
memcpy_vertex(dst + 3, dst + 2); dst[3].flags = GPU_CMD_VERTEX;
|
||||
|
||||
dst[2].flags = GPU_CMD_VERTEX_EOL;
|
||||
dst[1].flags = GPU_CMD_VERTEX;
|
||||
dst[0].flags = GPU_CMD_VERTEX;
|
||||
// TODO copy straight to dst??
|
||||
|
||||
|
||||
dst += 6;
|
||||
}
|
||||
}
|
||||
|
||||
GL_FORCE_INLINE void divide(SubmissionTarget* target) {
|
||||
TRACE();
|
||||
|
||||
/* Perform perspective divide on each vertex */
|
||||
Vertex* vertex = _glSubmissionTargetStart(target);
|
||||
|
||||
const float h = GetVideoMode()->height;
|
||||
|
||||
ITERATE(target->count) {
|
||||
const float f = MATH_Fast_Invert(vertex->w);
|
||||
|
||||
/* Convert to NDC and apply viewport */
|
||||
vertex->xyz[0] = MATH_fmac(
|
||||
VIEWPORT.hwidth, vertex->xyz[0] * f, VIEWPORT.x_plus_hwidth
|
||||
);
|
||||
vertex->xyz[1] = h - MATH_fmac(
|
||||
VIEWPORT.hheight, vertex->xyz[1] * f, VIEWPORT.y_plus_hheight
|
||||
);
|
||||
|
||||
/* Apply depth range */
|
||||
vertex->xyz[2] = MAX(
|
||||
1.0f - MATH_fmac(vertex->xyz[2] * f, 0.5f, 0.5f),
|
||||
PVR_MIN_Z
|
||||
);
|
||||
|
||||
++vertex;
|
||||
}
|
||||
}
|
||||
|
||||
GL_FORCE_INLINE int _calc_pvr_face_culling() {
|
||||
if(!_glIsCullingEnabled()) {
|
||||
return GPU_CULLING_SMALL;
|
||||
} else {
|
||||
if(_glGetCullFace() == GL_BACK) {
|
||||
return (_glGetFrontFace() == GL_CW) ? GPU_CULLING_CCW : GPU_CULLING_CW;
|
||||
} else {
|
||||
return (_glGetFrontFace() == GL_CCW) ? GPU_CULLING_CCW : GPU_CULLING_CW;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GL_FORCE_INLINE int _calc_pvr_depth_test() {
|
||||
if(!_glIsDepthTestEnabled()) {
|
||||
return GPU_DEPTHCMP_ALWAYS;
|
||||
}
|
||||
|
||||
switch(_glGetDepthFunc()) {
|
||||
case GL_NEVER:
|
||||
return GPU_DEPTHCMP_NEVER;
|
||||
case GL_LESS:
|
||||
return GPU_DEPTHCMP_GREATER;
|
||||
case GL_EQUAL:
|
||||
return GPU_DEPTHCMP_EQUAL;
|
||||
case GL_LEQUAL:
|
||||
return GPU_DEPTHCMP_GEQUAL;
|
||||
case GL_GREATER:
|
||||
return GPU_DEPTHCMP_LESS;
|
||||
case GL_NOTEQUAL:
|
||||
return GPU_DEPTHCMP_NOTEQUAL;
|
||||
case GL_GEQUAL:
|
||||
return GPU_DEPTHCMP_LEQUAL;
|
||||
break;
|
||||
case GL_ALWAYS:
|
||||
default:
|
||||
return GPU_DEPTHCMP_ALWAYS;
|
||||
}
|
||||
}
|
||||
|
||||
GL_FORCE_INLINE int _calcPVRBlendFactor(GLenum factor) {
|
||||
switch(factor) {
|
||||
case GL_ZERO:
|
||||
return GPU_BLEND_ZERO;
|
||||
case GL_SRC_ALPHA:
|
||||
return GPU_BLEND_SRCALPHA;
|
||||
case GL_DST_COLOR:
|
||||
return GPU_BLEND_DESTCOLOR;
|
||||
case GL_DST_ALPHA:
|
||||
return GPU_BLEND_DESTALPHA;
|
||||
case GL_ONE_MINUS_DST_COLOR:
|
||||
return GPU_BLEND_INVDESTCOLOR;
|
||||
case GL_ONE_MINUS_SRC_ALPHA:
|
||||
return GPU_BLEND_INVSRCALPHA;
|
||||
case GL_ONE_MINUS_DST_ALPHA:
|
||||
return GPU_BLEND_INVDESTALPHA;
|
||||
case GL_ONE:
|
||||
return GPU_BLEND_ONE;
|
||||
default:
|
||||
fprintf(stderr, "Invalid blend mode: %u\n", (unsigned int) factor);
|
||||
return GPU_BLEND_ONE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GL_FORCE_INLINE void _updatePVRBlend(PolyContext* context) {
|
||||
if(_glIsBlendingEnabled() || _glIsAlphaTestEnabled()) {
|
||||
context->gen.alpha = GPU_ALPHA_ENABLE;
|
||||
} else {
|
||||
context->gen.alpha = GPU_ALPHA_DISABLE;
|
||||
}
|
||||
|
||||
context->blend.src = _calcPVRBlendFactor(_glGetBlendSourceFactor());
|
||||
context->blend.dst = _calcPVRBlendFactor(_glGetBlendDestFactor());
|
||||
}
|
||||
|
||||
GL_FORCE_INLINE void apply_poly_header(PolyHeader* header, PolyList* activePolyList) {
|
||||
TRACE();
|
||||
|
||||
// Compile the header
|
||||
PolyContext ctx;
|
||||
memset(&ctx, 0, sizeof(PolyContext));
|
||||
|
||||
ctx.list_type = activePolyList->list_type;
|
||||
ctx.fmt.color = GPU_CLRFMT_ARGBPACKED;
|
||||
ctx.fmt.uv = GPU_UVFMT_32BIT;
|
||||
ctx.gen.color_clamp = GPU_CLRCLAMP_DISABLE;
|
||||
|
||||
ctx.gen.culling = _calc_pvr_face_culling();
|
||||
ctx.depth.comparison = _calc_pvr_depth_test();
|
||||
ctx.depth.write = _glIsDepthWriteEnabled() ? GPU_DEPTHWRITE_ENABLE : GPU_DEPTHWRITE_DISABLE;
|
||||
|
||||
ctx.gen.shading = (_glGetShadeModel() == GL_SMOOTH) ? GPU_SHADE_GOURAUD : GPU_SHADE_FLAT;
|
||||
|
||||
if(_glIsScissorTestEnabled()) {
|
||||
ctx.gen.clip_mode = GPU_USERCLIP_INSIDE;
|
||||
} else {
|
||||
ctx.gen.clip_mode = GPU_USERCLIP_DISABLE;
|
||||
}
|
||||
|
||||
if(_glIsFogEnabled()) {
|
||||
ctx.gen.fog_type = GPU_FOG_TABLE;
|
||||
} else {
|
||||
ctx.gen.fog_type = GPU_FOG_DISABLE;
|
||||
}
|
||||
|
||||
_updatePVRBlend(&ctx);
|
||||
|
||||
if(ctx.list_type == GPU_LIST_OP_POLY) {
|
||||
/* Opaque polys are always one/zero */
|
||||
ctx.blend.src = GPU_BLEND_ONE;
|
||||
ctx.blend.dst = GPU_BLEND_ZERO;
|
||||
} else if(ctx.list_type == GPU_LIST_PT_POLY) {
|
||||
/* Punch-through polys require fixed blending and depth modes */
|
||||
ctx.blend.src = GPU_BLEND_SRCALPHA;
|
||||
ctx.blend.dst = GPU_BLEND_INVSRCALPHA;
|
||||
ctx.depth.comparison = GPU_DEPTHCMP_LEQUAL;
|
||||
} else if(ctx.list_type == GPU_LIST_TR_POLY && AUTOSORT_ENABLED) {
|
||||
/* Autosort mode requires this mode for transparent polys */
|
||||
ctx.depth.comparison = GPU_DEPTHCMP_GEQUAL;
|
||||
}
|
||||
|
||||
_glUpdatePVRTextureContext(&ctx, 0);
|
||||
|
||||
CompilePolyHeader(header, &ctx);
|
||||
|
||||
/* Force bits 18 and 19 on to switch to 6 triangle strips */
|
||||
header->cmd |= 0xC0000;
|
||||
|
||||
/* Post-process the vertex list */
|
||||
/*
|
||||
* This is currently unnecessary. aligned_vector memsets the allocated objects
|
||||
* to zero, and we don't touch oargb, also, we don't *enable* oargb yet in the
|
||||
* pvr header so it should be ignored anyway. If this ever becomes a problem,
|
||||
* uncomment this.
|
||||
ClipVertex* vout = output;
|
||||
const ClipVertex* end = output + count;
|
||||
while(vout < end) {
|
||||
vout->oargb = 0;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
#define DEBUG_CLIPPING 0
|
||||
|
||||
|
||||
static SubmissionTarget SUBMISSION_TARGET;
|
||||
|
||||
|
||||
void _glInitSubmissionTarget() {
|
||||
SubmissionTarget* target = &SUBMISSION_TARGET;
|
||||
|
||||
target->count = 0;
|
||||
target->output = NULL;
|
||||
target->header_offset = target->start_offset = 0;
|
||||
}
|
||||
|
||||
|
||||
GL_FORCE_INLINE void submitVertices(GLenum mode, GLsizei first, GLuint count) {
|
||||
|
||||
SubmissionTarget* const target = &SUBMISSION_TARGET;
|
||||
|
||||
TRACE();
|
||||
|
||||
/* No vertices? Do nothing */
|
||||
if(!count) return;
|
||||
|
||||
target->output = _glActivePolyList();
|
||||
gl_assert(target->output);
|
||||
|
||||
uint32_t vector_size = aligned_vector_size(&target->output->vector);
|
||||
|
||||
GLboolean header_required = (vector_size == 0) || _glGPUStateIsDirty();
|
||||
|
||||
target->count = count * 6 / 4; // quads -> triangles
|
||||
target->header_offset = vector_size;
|
||||
target->start_offset = target->header_offset + (header_required ? 1 : 0);
|
||||
|
||||
gl_assert(target->header_offset >= 0);
|
||||
gl_assert(target->start_offset >= target->header_offset);
|
||||
gl_assert(target->count);
|
||||
|
||||
/* Make room for the vertices and header */
|
||||
aligned_vector_extend(&target->output->vector, target->count + (header_required));
|
||||
|
||||
if(header_required) {
|
||||
apply_poly_header(_glSubmissionTargetHeader(target), target->output);
|
||||
_glGPUStateMarkClean();
|
||||
}
|
||||
|
||||
generateQuads(target, first, count);
|
||||
}
|
||||
|
||||
void APIENTRY glDrawArrays(GLenum mode, GLint first, GLsizei count) {
|
||||
TRACE();
|
||||
submitVertices(mode, first, count);
|
||||
}
|
||||
|
||||
void APIENTRY glEnableClientState(GLenum cap) {
|
||||
TRACE();
|
||||
|
||||
switch(cap) {
|
||||
case GL_VERTEX_ARRAY:
|
||||
ENABLED_VERTEX_ATTRIBUTES |= VERTEX_ENABLED_FLAG;
|
||||
break;
|
||||
case GL_COLOR_ARRAY:
|
||||
ENABLED_VERTEX_ATTRIBUTES |= DIFFUSE_ENABLED_FLAG;
|
||||
break;
|
||||
case GL_TEXTURE_COORD_ARRAY:
|
||||
ENABLED_VERTEX_ATTRIBUTES |= UV_ENABLED_FLAG;
|
||||
break;
|
||||
default:
|
||||
_glKosThrowError(GL_INVALID_ENUM, __func__);
|
||||
}
|
||||
}
|
||||
|
||||
void APIENTRY glDisableClientState(GLenum cap) {
|
||||
TRACE();
|
||||
|
||||
switch(cap) {
|
||||
case GL_VERTEX_ARRAY:
|
||||
ENABLED_VERTEX_ATTRIBUTES &= ~VERTEX_ENABLED_FLAG;
|
||||
break;
|
||||
case GL_COLOR_ARRAY:
|
||||
ENABLED_VERTEX_ATTRIBUTES &= ~DIFFUSE_ENABLED_FLAG;
|
||||
break;
|
||||
case GL_TEXTURE_COORD_ARRAY:
|
||||
ENABLED_VERTEX_ATTRIBUTES &= ~UV_ENABLED_FLAG;
|
||||
break;
|
||||
default:
|
||||
_glKosThrowError(GL_INVALID_ENUM, __func__);
|
||||
}
|
||||
}
|
||||
|
||||
void APIENTRY glVertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid * pointer) {
|
||||
VERTEX_PTR = pointer;
|
||||
VERTEX_STRIDE = stride;
|
||||
}
|
32
third_party/gldc/src/error.c
vendored
Normal file
32
third_party/gldc/src/error.c
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
/* KallistiGL for KallistiOS ##version##
|
||||
|
||||
libgl/gl-error.c
|
||||
Copyright (C) 2014 Josh Pearson
|
||||
Copyright (C) 2016 Lawrence Sebald
|
||||
* Copyright (C) 2017 Luke Benstead
|
||||
|
||||
KOS Open GL State Machine Error Code Implementation.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "private.h"
|
||||
|
||||
GLenum LAST_ERROR = GL_NO_ERROR;
|
||||
char ERROR_FUNCTION[64] = { '\0' };
|
||||
|
||||
/* Quoth the GL Spec:
|
||||
When an error occurs, the error flag is set to the appropriate error code
|
||||
value. No other errors are recorded until glGetError is called, the error
|
||||
code is returned, and the flag is reset to GL_NO_ERROR.
|
||||
|
||||
So, we only record an error here if the error code is currently unset.
|
||||
Nothing in the spec requires recording multiple error flags, although it is
|
||||
allowed by the spec. We take the easy way out for now. */
|
||||
|
||||
|
||||
GLenum glGetError(void) {
|
||||
GLenum rv = LAST_ERROR;
|
||||
_glKosResetError();
|
||||
return rv;
|
||||
}
|
103
third_party/gldc/src/flush.c
vendored
Normal file
103
third_party/gldc/src/flush.c
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
#include "aligned_vector.h"
|
||||
#include "private.h"
|
||||
|
||||
PolyList OP_LIST;
|
||||
PolyList PT_LIST;
|
||||
PolyList TR_LIST;
|
||||
|
||||
/**
|
||||
* FAST_MODE will use invW for all Z coordinates sent to the
|
||||
* GPU.
|
||||
*
|
||||
* This will break orthographic mode so default is FALSE
|
||||
**/
|
||||
|
||||
#define FAST_MODE GL_FALSE
|
||||
|
||||
GLboolean AUTOSORT_ENABLED = GL_FALSE;
|
||||
|
||||
PolyList* _glOpaquePolyList() {
|
||||
return &OP_LIST;
|
||||
}
|
||||
|
||||
PolyList* _glPunchThruPolyList() {
|
||||
return &PT_LIST;
|
||||
}
|
||||
|
||||
PolyList *_glTransparentPolyList() {
|
||||
return &TR_LIST;
|
||||
}
|
||||
|
||||
|
||||
void APIENTRY glKosInitConfig(GLdcConfig* config) {
|
||||
config->autosort_enabled = GL_FALSE;
|
||||
config->fsaa_enabled = GL_FALSE;
|
||||
|
||||
config->initial_op_capacity = 1024 * 3;
|
||||
config->initial_pt_capacity = 512 * 3;
|
||||
config->initial_tr_capacity = 1024 * 3;
|
||||
}
|
||||
|
||||
void APIENTRY glKosInitEx(GLdcConfig* config) {
|
||||
TRACE();
|
||||
|
||||
puts("\nWelcome to GLdc!\n");
|
||||
|
||||
InitGPU(config->autosort_enabled, config->fsaa_enabled);
|
||||
|
||||
AUTOSORT_ENABLED = config->autosort_enabled;
|
||||
|
||||
_glInitSubmissionTarget();
|
||||
_glInitMatrices();
|
||||
_glInitAttributePointers();
|
||||
_glInitContext();
|
||||
_glInitTextures();
|
||||
|
||||
OP_LIST.list_type = GPU_LIST_OP_POLY;
|
||||
PT_LIST.list_type = GPU_LIST_PT_POLY;
|
||||
TR_LIST.list_type = GPU_LIST_TR_POLY;
|
||||
|
||||
aligned_vector_init(&OP_LIST.vector, sizeof(Vertex));
|
||||
aligned_vector_init(&PT_LIST.vector, sizeof(Vertex));
|
||||
aligned_vector_init(&TR_LIST.vector, sizeof(Vertex));
|
||||
|
||||
aligned_vector_reserve(&OP_LIST.vector, config->initial_op_capacity);
|
||||
aligned_vector_reserve(&PT_LIST.vector, config->initial_pt_capacity);
|
||||
aligned_vector_reserve(&TR_LIST.vector, config->initial_tr_capacity);
|
||||
}
|
||||
|
||||
void APIENTRY glKosInit() {
|
||||
GLdcConfig config;
|
||||
glKosInitConfig(&config);
|
||||
glKosInitEx(&config);
|
||||
}
|
||||
|
||||
void APIENTRY glKosSwapBuffers() {
|
||||
TRACE();
|
||||
|
||||
SceneBegin();
|
||||
if(aligned_vector_header(&OP_LIST.vector)->size > 2) {
|
||||
SceneListBegin(GPU_LIST_OP_POLY);
|
||||
SceneListSubmit((Vertex*) aligned_vector_front(&OP_LIST.vector), aligned_vector_size(&OP_LIST.vector));
|
||||
SceneListFinish();
|
||||
}
|
||||
|
||||
if(aligned_vector_header(&PT_LIST.vector)->size > 2) {
|
||||
SceneListBegin(GPU_LIST_PT_POLY);
|
||||
SceneListSubmit((Vertex*) aligned_vector_front(&PT_LIST.vector), aligned_vector_size(&PT_LIST.vector));
|
||||
SceneListFinish();
|
||||
}
|
||||
|
||||
if(aligned_vector_header(&TR_LIST.vector)->size > 2) {
|
||||
SceneListBegin(GPU_LIST_TR_POLY);
|
||||
SceneListSubmit((Vertex*) aligned_vector_front(&TR_LIST.vector), aligned_vector_size(&TR_LIST.vector));
|
||||
SceneListFinish();
|
||||
}
|
||||
SceneFinish();
|
||||
|
||||
aligned_vector_clear(&OP_LIST.vector);
|
||||
aligned_vector_clear(&PT_LIST.vector);
|
||||
aligned_vector_clear(&TR_LIST.vector);
|
||||
|
||||
_glApplyScissor(true);
|
||||
}
|
20
third_party/gldc/src/gl_assert.h
vendored
Normal file
20
third_party/gldc/src/gl_assert.h
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
|
||||
#ifndef NDEBUG
|
||||
/* We're debugging, use normal assert */
|
||||
#include <assert.h>
|
||||
#define gl_assert assert
|
||||
#else
|
||||
/* Release mode, use our custom assert */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define gl_assert(x) \
|
||||
do {\
|
||||
if(!(x)) {\
|
||||
fprintf(stderr, "Assertion failed at %s:%d\n", __FILE__, __LINE__);\
|
||||
exit(1);\
|
||||
}\
|
||||
} while(0); \
|
||||
|
||||
#endif
|
||||
|
27
third_party/gldc/src/matrix.c
vendored
Normal file
27
third_party/gldc/src/matrix.c
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "private.h"
|
||||
|
||||
Viewport VIEWPORT = {
|
||||
0, 0, 640, 480, 320.0f, 240.0f, 320.0f, 240.0f
|
||||
};
|
||||
|
||||
void _glInitMatrices() {
|
||||
const VideoMode* vid_mode = GetVideoMode();
|
||||
|
||||
glViewport(0, 0, vid_mode->width, vid_mode->height);
|
||||
}
|
||||
|
||||
/* Set the GL viewport */
|
||||
void APIENTRY glViewport(GLint x, GLint y, GLsizei width, GLsizei height) {
|
||||
VIEWPORT.x = x;
|
||||
VIEWPORT.y = y;
|
||||
VIEWPORT.width = width;
|
||||
VIEWPORT.height = height;
|
||||
VIEWPORT.hwidth = ((GLfloat) VIEWPORT.width) * 0.5f;
|
||||
VIEWPORT.hheight = ((GLfloat) VIEWPORT.height) * 0.5f;
|
||||
VIEWPORT.x_plus_hwidth = VIEWPORT.x + VIEWPORT.hwidth;
|
||||
VIEWPORT.y_plus_hheight = VIEWPORT.y + VIEWPORT.hheight;
|
||||
}
|
90
third_party/gldc/src/named_array.c
vendored
Normal file
90
third_party/gldc/src/named_array.c
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#ifndef __APPLE__
|
||||
#include <malloc.h>
|
||||
#else
|
||||
/* Linux + Kos define this, OSX does not, so just use malloc there */
|
||||
#define memalign(x, size) malloc((size))
|
||||
#endif
|
||||
|
||||
#include "named_array.h"
|
||||
|
||||
void named_array_init(NamedArray* array, unsigned int element_size, unsigned int max_elements) {
|
||||
array->element_size = element_size;
|
||||
array->max_element_count = max_elements;
|
||||
|
||||
array->marker_count = (unsigned char)((max_elements+8-1)/8);
|
||||
|
||||
#ifdef _arch_dreamcast
|
||||
// Use 32-bit aligned memory on the Dreamcast
|
||||
array->elements = (unsigned char*) memalign(0x20, element_size * max_elements);
|
||||
array->used_markers = (unsigned char*) memalign(0x20, array->marker_count);
|
||||
#else
|
||||
array->elements = (unsigned char*) malloc(element_size * max_elements);
|
||||
array->used_markers = (unsigned char*) malloc(array->marker_count);
|
||||
#endif
|
||||
memset(array->used_markers, 0, sizeof(unsigned char) * array->marker_count);
|
||||
}
|
||||
|
||||
void* named_array_alloc(NamedArray* array, unsigned int* new_id) {
|
||||
unsigned int i = 0, j = 0;
|
||||
for(i = 0; i < array->marker_count; ++i) {
|
||||
for(j = 0; j < 8; ++j) {
|
||||
unsigned int id = (i * 8) + j;
|
||||
if(!named_array_used(array, id)) {
|
||||
array->used_markers[i] |= (unsigned char) 1 << j;
|
||||
*new_id = id;
|
||||
unsigned char* ptr = &array->elements[id * array->element_size];
|
||||
memset(ptr, 0, array->element_size);
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void* named_array_reserve(NamedArray* array, unsigned int id) {
|
||||
if(!named_array_used(array, id)) {
|
||||
unsigned int j = (id % 8);
|
||||
unsigned int i = id / 8;
|
||||
|
||||
assert(!named_array_used(array, id));
|
||||
array->used_markers[i] |= (unsigned char) 1 << j;
|
||||
assert(named_array_used(array, id));
|
||||
|
||||
unsigned char* ptr = &array->elements[id * array->element_size];
|
||||
memset(ptr, 0, array->element_size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
return named_array_get(array, id);
|
||||
}
|
||||
|
||||
void named_array_release(NamedArray* array, unsigned int new_id) {
|
||||
unsigned int i = new_id / 8;
|
||||
unsigned int j = new_id % 8;
|
||||
array->used_markers[i] &= (unsigned char) ~(1 << j);
|
||||
}
|
||||
|
||||
void* named_array_get(NamedArray* array, unsigned int id) {
|
||||
if(!named_array_used(array, id)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &array->elements[id * array->element_size];
|
||||
}
|
||||
|
||||
void named_array_cleanup(NamedArray* array) {
|
||||
free(array->elements);
|
||||
free(array->used_markers);
|
||||
array->elements = NULL;
|
||||
array->used_markers = NULL;
|
||||
array->element_size = array->max_element_count = 0;
|
||||
array->marker_count = 0;
|
||||
}
|
||||
|
38
third_party/gldc/src/named_array.h
vendored
Normal file
38
third_party/gldc/src/named_array.h
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef NAMED_ARRAY_H
|
||||
#define NAMED_ARRAY_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
unsigned int element_size;
|
||||
unsigned int max_element_count;
|
||||
unsigned char* elements;
|
||||
unsigned char* used_markers;
|
||||
unsigned char marker_count;
|
||||
} NamedArray;
|
||||
|
||||
void named_array_init(NamedArray* array, unsigned int element_size, unsigned int max_elements);
|
||||
static inline char named_array_used(NamedArray* array, unsigned int id) {
|
||||
const unsigned int i = id / 8;
|
||||
const unsigned int j = id % 8;
|
||||
|
||||
unsigned char v = array->used_markers[i] & (unsigned char) (1 << j);
|
||||
return !!(v);
|
||||
}
|
||||
|
||||
void* named_array_alloc(NamedArray* array, unsigned int* new_id);
|
||||
void* named_array_reserve(NamedArray* array, unsigned int id);
|
||||
|
||||
void named_array_release(NamedArray* array, unsigned int new_id);
|
||||
void* named_array_get(NamedArray* array, unsigned int id);
|
||||
void named_array_cleanup(NamedArray* array);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // NAMED_ARRAY_H
|
433
third_party/gldc/src/platform.h
vendored
Normal file
433
third_party/gldc/src/platform.h
vendored
Normal file
@ -0,0 +1,433 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "gl_assert.h"
|
||||
#include "types.h"
|
||||
|
||||
#define MEMSET(dst, v, size) memset((dst), (v), (size))
|
||||
|
||||
typedef enum GPUAlpha {
|
||||
GPU_ALPHA_DISABLE = 0,
|
||||
GPU_ALPHA_ENABLE = 1
|
||||
} GPUAlpha;
|
||||
|
||||
typedef enum GPUTexture {
|
||||
GPU_TEXTURE_DISABLE = 0,
|
||||
GPU_TEXTURE_ENABLE = 1
|
||||
} GPUTexture;
|
||||
|
||||
typedef enum GPUTextureAlpha {
|
||||
GPU_TXRALPHA_DISABLE = 1,
|
||||
GPU_TXRALPHA_ENABLE = 0
|
||||
} GPUTextureAlpha;
|
||||
|
||||
typedef enum GPUList {
|
||||
GPU_LIST_OP_POLY = 0,
|
||||
GPU_LIST_OP_MOD = 1,
|
||||
GPU_LIST_TR_POLY = 2,
|
||||
GPU_LIST_TR_MOD = 3,
|
||||
GPU_LIST_PT_POLY = 4
|
||||
} GPUList;
|
||||
|
||||
typedef enum GPUBlend {
|
||||
GPU_BLEND_ZERO = 0,
|
||||
GPU_BLEND_ONE = 1,
|
||||
GPU_BLEND_DESTCOLOR = 2,
|
||||
GPU_BLEND_INVDESTCOLOR = 3,
|
||||
GPU_BLEND_SRCALPHA = 4,
|
||||
GPU_BLEND_INVSRCALPHA = 5,
|
||||
GPU_BLEND_DESTALPHA = 6,
|
||||
GPU_BLEND_INVDESTALPHA = 7
|
||||
} GPUBlend;
|
||||
|
||||
typedef enum GPUDepthCompare {
|
||||
GPU_DEPTHCMP_NEVER = 0,
|
||||
GPU_DEPTHCMP_LESS = 1,
|
||||
GPU_DEPTHCMP_EQUAL = 2,
|
||||
GPU_DEPTHCMP_LEQUAL = 3,
|
||||
GPU_DEPTHCMP_GREATER = 4,
|
||||
GPU_DEPTHCMP_NOTEQUAL = 5,
|
||||
GPU_DEPTHCMP_GEQUAL = 6,
|
||||
GPU_DEPTHCMP_ALWAYS = 7
|
||||
} GPUDepthCompare;
|
||||
|
||||
typedef enum GPUTextureFormat {
|
||||
GPU_TXRFMT_NONE,
|
||||
GPU_TXRFMT_VQ_DISABLE = (0 << 30),
|
||||
GPU_TXRFMT_VQ_ENABLE = (1 << 30),
|
||||
GPU_TXRFMT_ARGB1555 = (0 << 27),
|
||||
GPU_TXRFMT_RGB565 = (1 << 27),
|
||||
GPU_TXRFMT_ARGB4444 = (2 << 27),
|
||||
GPU_TXRFMT_YUV422 = (3 << 27),
|
||||
GPU_TXRFMT_BUMP = (4 << 27),
|
||||
GPU_TXRFMT_PAL4BPP = (5 << 27),
|
||||
GPU_TXRFMT_PAL8BPP = (6 << 27),
|
||||
GPU_TXRFMT_TWIDDLED = (0 << 26),
|
||||
GPU_TXRFMT_NONTWIDDLED = (1 << 26),
|
||||
GPU_TXRFMT_NOSTRIDE = (0 << 21),
|
||||
GPU_TXRFMT_STRIDE = (1 << 21)
|
||||
} GPUTextureFormat;
|
||||
|
||||
typedef enum GPUCulling {
|
||||
GPU_CULLING_NONE = 0,
|
||||
GPU_CULLING_SMALL = 1,
|
||||
GPU_CULLING_CCW = 2,
|
||||
GPU_CULLING_CW = 3
|
||||
} GPUCulling;
|
||||
|
||||
typedef enum GPUUVFlip {
|
||||
GPU_UVFLIP_NONE = 0,
|
||||
GPU_UVFLIP_V = 1,
|
||||
GPU_UVFLIP_U = 2,
|
||||
GPU_UVFLIP_UV = 3
|
||||
} GPUUVFlip;
|
||||
|
||||
typedef enum GPUUVClamp {
|
||||
GPU_UVCLAMP_NONE = 0,
|
||||
GPU_UVCLAMP_V = 1,
|
||||
GPU_UVCLAMP_U = 2,
|
||||
GPU_UVCLAMP_UV = 3
|
||||
} GPUUVClamp;
|
||||
|
||||
typedef enum GPUColorClamp {
|
||||
GPU_CLRCLAMP_DISABLE = 0,
|
||||
GPU_CLRCLAMP_ENABLE = 1
|
||||
} GPUColorClamp;
|
||||
|
||||
typedef enum GPUFilter {
|
||||
GPU_FILTER_NEAREST = 0,
|
||||
GPU_FILTER_BILINEAR = 2,
|
||||
GPU_FILTER_TRILINEAR1 = 4,
|
||||
GPU_FILTER_TRILINEAR2 = 6
|
||||
} GPUFilter;
|
||||
|
||||
typedef enum GPUDepthWrite {
|
||||
GPU_DEPTHWRITE_ENABLE = 0,
|
||||
GPU_DEPTHWRITE_DISABLE = 1
|
||||
} GPUDepthWrite;
|
||||
|
||||
typedef enum GPUUserClip {
|
||||
GPU_USERCLIP_DISABLE = 0,
|
||||
GPU_USERCLIP_INSIDE = 2,
|
||||
GPU_USERCLIP_OUTSIDE = 3
|
||||
} GPUUserClip;
|
||||
|
||||
typedef enum GPUColorFormat {
|
||||
GPU_CLRFMT_ARGBPACKED = 0,
|
||||
GPU_CLRFMT_4FLOATS = 1,
|
||||
GPU_CLRFMT_INTENSITY = 2,
|
||||
GPU_CLRFMT_INTENSITY_PREV = 3
|
||||
} GPUColorFormat;
|
||||
|
||||
typedef enum GPUUVFormat {
|
||||
GPU_UVFMT_32BIT = 0,
|
||||
GPU_UVFMT_16BIT = 1
|
||||
} GPUUVFormat;
|
||||
|
||||
typedef enum GPUFog {
|
||||
GPU_FOG_TABLE = 0,
|
||||
GPU_FOG_VERTEX = 1,
|
||||
GPU_FOG_DISABLE = 2,
|
||||
GPU_FOG_TABLE2 = 3
|
||||
} GPUFog;
|
||||
|
||||
typedef enum GPUShade {
|
||||
GPU_SHADE_FLAT = 0,
|
||||
GPU_SHADE_GOURAUD = 1
|
||||
} GPUShade;
|
||||
|
||||
typedef enum GPUTextureEnv {
|
||||
GPU_TXRENV_REPLACE = 0,
|
||||
GPU_TXRENV_MODULATE = 1,
|
||||
GPU_TXRENV_DECAL = 2,
|
||||
GPU_TXRENV_MODULATEALPHA = 3
|
||||
} GPUTextureEnv;
|
||||
|
||||
typedef struct VideoMode {
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
} VideoMode;
|
||||
|
||||
const VideoMode* GetVideoMode();
|
||||
|
||||
/* Duplication of pvr_poly_cxt_t from KOS so that we can
|
||||
* compile on non-KOS platforms for testing */
|
||||
|
||||
typedef struct {
|
||||
GPUList list_type;
|
||||
|
||||
struct {
|
||||
int alpha;
|
||||
int shading;
|
||||
int fog_type;
|
||||
int culling;
|
||||
int color_clamp;
|
||||
int clip_mode;
|
||||
int modifier_mode;
|
||||
int specular;
|
||||
int alpha2;
|
||||
int fog_type2;
|
||||
int color_clamp2;
|
||||
} gen;
|
||||
struct {
|
||||
int src;
|
||||
int dst;
|
||||
int src_enable;
|
||||
int dst_enable;
|
||||
int src2;
|
||||
int dst2;
|
||||
int src_enable2;
|
||||
int dst_enable2;
|
||||
} blend;
|
||||
struct {
|
||||
int color;
|
||||
int uv;
|
||||
int modifier;
|
||||
} fmt;
|
||||
struct {
|
||||
int comparison;
|
||||
int write;
|
||||
} depth;
|
||||
struct {
|
||||
int enable;
|
||||
int filter;
|
||||
int mipmap;
|
||||
int mipmap_bias;
|
||||
int uv_flip;
|
||||
int uv_clamp;
|
||||
int alpha;
|
||||
int env;
|
||||
int width;
|
||||
int height;
|
||||
int format;
|
||||
void* base;
|
||||
} txr;
|
||||
struct {
|
||||
int enable;
|
||||
int filter;
|
||||
int mipmap;
|
||||
int mipmap_bias;
|
||||
int uv_flip;
|
||||
int uv_clamp;
|
||||
int alpha;
|
||||
int env;
|
||||
int width;
|
||||
int height;
|
||||
int format;
|
||||
void* base;
|
||||
} txr2;
|
||||
} PolyContext;
|
||||
|
||||
typedef struct {
|
||||
uint32_t cmd;
|
||||
uint32_t mode1;
|
||||
uint32_t mode2;
|
||||
uint32_t mode3;
|
||||
uint32_t d1;
|
||||
uint32_t d2;
|
||||
uint32_t d3;
|
||||
uint32_t d4;
|
||||
} PolyHeader;
|
||||
|
||||
enum GPUCommand {
|
||||
GPU_CMD_POLYHDR = 0x80840000,
|
||||
GPU_CMD_VERTEX = 0xe0000000,
|
||||
GPU_CMD_VERTEX_EOL = 0xf0000000,
|
||||
GPU_CMD_USERCLIP = 0x20000000,
|
||||
GPU_CMD_MODIFIER = 0x80000000,
|
||||
GPU_CMD_SPRITE = 0xA0000000
|
||||
};
|
||||
|
||||
typedef float Matrix4x4[16];
|
||||
|
||||
void SceneBegin();
|
||||
|
||||
void SceneListBegin(GPUList list);
|
||||
void SceneListSubmit(Vertex* v2, int n);
|
||||
void SceneListFinish();
|
||||
|
||||
void SceneFinish();
|
||||
|
||||
#define GPU_TA_CMD_TYPE_SHIFT 24
|
||||
#define GPU_TA_CMD_TYPE_MASK (7 << GPU_TA_CMD_TYPE_SHIFT)
|
||||
|
||||
#define GPU_TA_CMD_USERCLIP_SHIFT 16
|
||||
#define GPU_TA_CMD_USERCLIP_MASK (3 << GPU_TA_CMD_USERCLIP_SHIFT)
|
||||
|
||||
#define GPU_TA_CMD_CLRFMT_SHIFT 4
|
||||
#define GPU_TA_CMD_CLRFMT_MASK (7 << GPU_TA_CMD_CLRFMT_SHIFT)
|
||||
|
||||
#define GPU_TA_CMD_SPECULAR_SHIFT 2
|
||||
#define GPU_TA_CMD_SPECULAR_MASK (1 << GPU_TA_CMD_SPECULAR_SHIFT)
|
||||
|
||||
#define GPU_TA_CMD_SHADE_SHIFT 1
|
||||
#define GPU_TA_CMD_SHADE_MASK (1 << GPU_TA_CMD_SHADE_SHIFT)
|
||||
|
||||
#define GPU_TA_CMD_UVFMT_SHIFT 0
|
||||
#define GPU_TA_CMD_UVFMT_MASK (1 << GPU_TA_CMD_UVFMT_SHIFT)
|
||||
|
||||
#define GPU_TA_CMD_MODIFIER_SHIFT 7
|
||||
#define GPU_TA_CMD_MODIFIER_MASK (1 << GPU_TA_CMD_MODIFIER_SHIFT)
|
||||
|
||||
#define GPU_TA_CMD_MODIFIERMODE_SHIFT 6
|
||||
#define GPU_TA_CMD_MODIFIERMODE_MASK (1 << GPU_TA_CMD_MODIFIERMODE_SHIFT)
|
||||
|
||||
#define GPU_TA_PM1_DEPTHCMP_SHIFT 29
|
||||
#define GPU_TA_PM1_DEPTHCMP_MASK (7 << GPU_TA_PM1_DEPTHCMP_SHIFT)
|
||||
|
||||
#define GPU_TA_PM1_CULLING_SHIFT 27
|
||||
#define GPU_TA_PM1_CULLING_MASK (3 << GPU_TA_PM1_CULLING_SHIFT)
|
||||
|
||||
#define GPU_TA_PM1_DEPTHWRITE_SHIFT 26
|
||||
#define GPU_TA_PM1_DEPTHWRITE_MASK (1 << GPU_TA_PM1_DEPTHWRITE_SHIFT)
|
||||
|
||||
#define GPU_TA_PM1_TXRENABLE_SHIFT 25
|
||||
#define GPU_TA_PM1_TXRENABLE_MASK (1 << GPU_TA_PM1_TXRENABLE_SHIFT)
|
||||
|
||||
#define GPU_TA_PM1_MODIFIERINST_SHIFT 29
|
||||
#define GPU_TA_PM1_MODIFIERINST_MASK (3 << GPU_TA_PM1_MODIFIERINST_SHIFT)
|
||||
|
||||
#define GPU_TA_PM2_SRCBLEND_SHIFT 29
|
||||
#define GPU_TA_PM2_SRCBLEND_MASK (7 << GPU_TA_PM2_SRCBLEND_SHIFT)
|
||||
|
||||
#define GPU_TA_PM2_DSTBLEND_SHIFT 26
|
||||
#define GPU_TA_PM2_DSTBLEND_MASK (7 << GPU_TA_PM2_DSTBLEND_SHIFT)
|
||||
|
||||
#define GPU_TA_PM2_SRCENABLE_SHIFT 25
|
||||
#define GPU_TA_PM2_SRCENABLE_MASK (1 << GPU_TA_PM2_SRCENABLE_SHIFT)
|
||||
|
||||
#define GPU_TA_PM2_DSTENABLE_SHIFT 24
|
||||
#define GPU_TA_PM2_DSTENABLE_MASK (1 << GPU_TA_PM2_DSTENABLE_SHIFT)
|
||||
|
||||
#define GPU_TA_PM2_FOG_SHIFT 22
|
||||
#define GPU_TA_PM2_FOG_MASK (3 << GPU_TA_PM2_FOG_SHIFT)
|
||||
|
||||
#define GPU_TA_PM2_CLAMP_SHIFT 21
|
||||
#define GPU_TA_PM2_CLAMP_MASK (1 << GPU_TA_PM2_CLAMP_SHIFT)
|
||||
|
||||
#define GPU_TA_PM2_ALPHA_SHIFT 20
|
||||
#define GPU_TA_PM2_ALPHA_MASK (1 << GPU_TA_PM2_ALPHA_SHIFT)
|
||||
|
||||
#define GPU_TA_PM2_TXRALPHA_SHIFT 19
|
||||
#define GPU_TA_PM2_TXRALPHA_MASK (1 << GPU_TA_PM2_TXRALPHA_SHIFT)
|
||||
|
||||
#define GPU_TA_PM2_UVFLIP_SHIFT 17
|
||||
#define GPU_TA_PM2_UVFLIP_MASK (3 << GPU_TA_PM2_UVFLIP_SHIFT)
|
||||
|
||||
#define GPU_TA_PM2_UVCLAMP_SHIFT 15
|
||||
#define GPU_TA_PM2_UVCLAMP_MASK (3 << GPU_TA_PM2_UVCLAMP_SHIFT)
|
||||
|
||||
#define GPU_TA_PM2_FILTER_SHIFT 12
|
||||
#define GPU_TA_PM2_FILTER_MASK (7 << GPU_TA_PM2_FILTER_SHIFT)
|
||||
|
||||
#define GPU_TA_PM2_MIPBIAS_SHIFT 8
|
||||
#define GPU_TA_PM2_MIPBIAS_MASK (15 << GPU_TA_PM2_MIPBIAS_SHIFT)
|
||||
|
||||
#define GPU_TA_PM2_TXRENV_SHIFT 6
|
||||
#define GPU_TA_PM2_TXRENV_MASK (3 << GPU_TA_PM2_TXRENV_SHIFT)
|
||||
|
||||
#define GPU_TA_PM2_USIZE_SHIFT 3
|
||||
#define GPU_TA_PM2_USIZE_MASK (7 << GPU_TA_PM2_USIZE_SHIFT)
|
||||
|
||||
#define GPU_TA_PM2_VSIZE_SHIFT 0
|
||||
#define GPU_TA_PM2_VSIZE_MASK (7 << GPU_TA_PM2_VSIZE_SHIFT)
|
||||
|
||||
#define GPU_TA_PM3_MIPMAP_SHIFT 31
|
||||
#define GPU_TA_PM3_MIPMAP_MASK (1 << GPU_TA_PM3_MIPMAP_SHIFT)
|
||||
|
||||
#define GPU_TA_PM3_TXRFMT_SHIFT 0
|
||||
#define GPU_TA_PM3_TXRFMT_MASK 0xffffffff
|
||||
|
||||
static inline int DimensionFlag(const int w) {
|
||||
switch(w) {
|
||||
case 16: return 1;
|
||||
case 32: return 2;
|
||||
case 64: return 3;
|
||||
case 128: return 4;
|
||||
case 256: return 5;
|
||||
case 512: return 6;
|
||||
case 1024: return 7;
|
||||
case 8:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Compile a polygon context into a polygon header */
|
||||
static inline void CompilePolyHeader(PolyHeader *dst, const PolyContext *src) {
|
||||
uint32_t txr_base;
|
||||
|
||||
/* Basically we just take each parameter, clip it, shift it
|
||||
into place, and OR it into the final result. */
|
||||
|
||||
/* The base values for CMD */
|
||||
dst->cmd = GPU_CMD_POLYHDR;
|
||||
|
||||
dst->cmd |= src->txr.enable << 3;
|
||||
|
||||
/* Or in the list type, shading type, color and UV formats */
|
||||
dst->cmd |= (src->list_type << GPU_TA_CMD_TYPE_SHIFT) & GPU_TA_CMD_TYPE_MASK;
|
||||
dst->cmd |= (src->fmt.color << GPU_TA_CMD_CLRFMT_SHIFT) & GPU_TA_CMD_CLRFMT_MASK;
|
||||
dst->cmd |= (src->gen.shading << GPU_TA_CMD_SHADE_SHIFT) & GPU_TA_CMD_SHADE_MASK;
|
||||
dst->cmd |= (src->fmt.uv << GPU_TA_CMD_UVFMT_SHIFT) & GPU_TA_CMD_UVFMT_MASK;
|
||||
dst->cmd |= (src->gen.clip_mode << GPU_TA_CMD_USERCLIP_SHIFT) & GPU_TA_CMD_USERCLIP_MASK;
|
||||
dst->cmd |= (src->fmt.modifier << GPU_TA_CMD_MODIFIER_SHIFT) & GPU_TA_CMD_MODIFIER_MASK;
|
||||
dst->cmd |= (src->gen.modifier_mode << GPU_TA_CMD_MODIFIERMODE_SHIFT) & GPU_TA_CMD_MODIFIERMODE_MASK;
|
||||
dst->cmd |= (src->gen.specular << GPU_TA_CMD_SPECULAR_SHIFT) & GPU_TA_CMD_SPECULAR_MASK;
|
||||
|
||||
/* Polygon mode 1 */
|
||||
dst->mode1 = (src->depth.comparison << GPU_TA_PM1_DEPTHCMP_SHIFT) & GPU_TA_PM1_DEPTHCMP_MASK;
|
||||
dst->mode1 |= (src->gen.culling << GPU_TA_PM1_CULLING_SHIFT) & GPU_TA_PM1_CULLING_MASK;
|
||||
dst->mode1 |= (src->depth.write << GPU_TA_PM1_DEPTHWRITE_SHIFT) & GPU_TA_PM1_DEPTHWRITE_MASK;
|
||||
dst->mode1 |= (src->txr.enable << GPU_TA_PM1_TXRENABLE_SHIFT) & GPU_TA_PM1_TXRENABLE_MASK;
|
||||
|
||||
/* Polygon mode 2 */
|
||||
dst->mode2 = (src->blend.src << GPU_TA_PM2_SRCBLEND_SHIFT) & GPU_TA_PM2_SRCBLEND_MASK;
|
||||
dst->mode2 |= (src->blend.dst << GPU_TA_PM2_DSTBLEND_SHIFT) & GPU_TA_PM2_DSTBLEND_MASK;
|
||||
dst->mode2 |= (src->blend.src_enable << GPU_TA_PM2_SRCENABLE_SHIFT) & GPU_TA_PM2_SRCENABLE_MASK;
|
||||
dst->mode2 |= (src->blend.dst_enable << GPU_TA_PM2_DSTENABLE_SHIFT) & GPU_TA_PM2_DSTENABLE_MASK;
|
||||
dst->mode2 |= (src->gen.fog_type << GPU_TA_PM2_FOG_SHIFT) & GPU_TA_PM2_FOG_MASK;
|
||||
dst->mode2 |= (src->gen.color_clamp << GPU_TA_PM2_CLAMP_SHIFT) & GPU_TA_PM2_CLAMP_MASK;
|
||||
dst->mode2 |= (src->gen.alpha << GPU_TA_PM2_ALPHA_SHIFT) & GPU_TA_PM2_ALPHA_MASK;
|
||||
|
||||
if(src->txr.enable == GPU_TEXTURE_DISABLE) {
|
||||
dst->mode3 = 0;
|
||||
}
|
||||
else {
|
||||
dst->mode2 |= (src->txr.alpha << GPU_TA_PM2_TXRALPHA_SHIFT) & GPU_TA_PM2_TXRALPHA_MASK;
|
||||
dst->mode2 |= (src->txr.uv_flip << GPU_TA_PM2_UVFLIP_SHIFT) & GPU_TA_PM2_UVFLIP_MASK;
|
||||
dst->mode2 |= (src->txr.uv_clamp << GPU_TA_PM2_UVCLAMP_SHIFT) & GPU_TA_PM2_UVCLAMP_MASK;
|
||||
dst->mode2 |= (src->txr.filter << GPU_TA_PM2_FILTER_SHIFT) & GPU_TA_PM2_FILTER_MASK;
|
||||
dst->mode2 |= (src->txr.mipmap_bias << GPU_TA_PM2_MIPBIAS_SHIFT) & GPU_TA_PM2_MIPBIAS_MASK;
|
||||
dst->mode2 |= (src->txr.env << GPU_TA_PM2_TXRENV_SHIFT) & GPU_TA_PM2_TXRENV_MASK;
|
||||
|
||||
dst->mode2 |= (DimensionFlag(src->txr.width) << GPU_TA_PM2_USIZE_SHIFT) & GPU_TA_PM2_USIZE_MASK;
|
||||
dst->mode2 |= (DimensionFlag(src->txr.height) << GPU_TA_PM2_VSIZE_SHIFT) & GPU_TA_PM2_VSIZE_MASK;
|
||||
|
||||
/* Polygon mode 3 */
|
||||
dst->mode3 = (src->txr.mipmap << GPU_TA_PM3_MIPMAP_SHIFT) & GPU_TA_PM3_MIPMAP_MASK;
|
||||
dst->mode3 |= (src->txr.format << GPU_TA_PM3_TXRFMT_SHIFT) & GPU_TA_PM3_TXRFMT_MASK;
|
||||
|
||||
/* Convert the texture address */
|
||||
txr_base = (uint32_t) src->txr.base;
|
||||
txr_base = (txr_base & 0x00fffff8) >> 3;
|
||||
dst->mode3 |= txr_base;
|
||||
}
|
||||
|
||||
if(src->fmt.modifier && src->gen.modifier_mode) {
|
||||
/* If we're affected by a modifier volume, silently promote the header
|
||||
to the one that is affected by a modifier volume. */
|
||||
dst->d1 = dst->mode2;
|
||||
dst->d2 = dst->mode3;
|
||||
}
|
||||
else {
|
||||
dst->d1 = dst->d2 = 0xffffffff;
|
||||
}
|
||||
|
||||
dst->d3 = dst->d4 = 0xffffffff;
|
||||
}
|
||||
|
||||
#include "sh4.h"
|
323
third_party/gldc/src/private.h
vendored
Normal file
323
third_party/gldc/src/private.h
vendored
Normal file
@ -0,0 +1,323 @@
|
||||
#ifndef PRIVATE_H
|
||||
#define PRIVATE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "gl_assert.h"
|
||||
#include "platform.h"
|
||||
#include "types.h"
|
||||
|
||||
#include "../include/gldc.h"
|
||||
|
||||
#include "aligned_vector.h"
|
||||
#include "named_array.h"
|
||||
|
||||
/* This figure is derived from the needs of Quake 1 */
|
||||
#define MAX_TEXTURE_COUNT 1088
|
||||
|
||||
|
||||
extern void* memcpy4 (void *dest, const void *src, size_t count);
|
||||
|
||||
#define GL_NO_INSTRUMENT inline __attribute__((no_instrument_function))
|
||||
#define GL_INLINE_DEBUG GL_NO_INSTRUMENT __attribute__((always_inline))
|
||||
#define GL_FORCE_INLINE static GL_INLINE_DEBUG
|
||||
#define _GL_UNUSED(x) (void)(x)
|
||||
|
||||
#define _PACK4(v) ((v * 0xF) / 0xFF)
|
||||
#define PACK_ARGB4444(a,r,g,b) (_PACK4(a) << 12) | (_PACK4(r) << 8) | (_PACK4(g) << 4) | (_PACK4(b))
|
||||
#define PACK_ARGB8888(a,r,g,b) ( ((a & 0xFF) << 24) | ((r & 0xFF) << 16) | ((g & 0xFF) << 8) | (b & 0xFF) )
|
||||
#define PACK_ARGB1555(a,r,g,b) \
|
||||
(((GLushort)(a > 0) << 15) | (((GLushort) r >> 3) << 10) | (((GLushort)g >> 3) << 5) | ((GLushort)b >> 3))
|
||||
|
||||
#define PACK_RGB565(r,g,b) \
|
||||
((((GLushort)r & 0xf8) << 8) | (((GLushort) g & 0xfc) << 3) | ((GLushort) b >> 3))
|
||||
|
||||
#define TRACE_ENABLED 0
|
||||
#define TRACE() if(TRACE_ENABLED) {fprintf(stderr, "%s\n", __func__);} (void) 0
|
||||
|
||||
#define VERTEX_ENABLED_FLAG (1 << 0)
|
||||
#define UV_ENABLED_FLAG (1 << 1)
|
||||
#define ST_ENABLED_FLAG (1 << 2)
|
||||
#define DIFFUSE_ENABLED_FLAG (1 << 3)
|
||||
|
||||
#define MAX_TEXTURE_SIZE 1024
|
||||
|
||||
typedef struct {
|
||||
unsigned int flags; /* Constant PVR_CMD_USERCLIP */
|
||||
unsigned int d1, d2, d3; /* Ignored for this type */
|
||||
unsigned int sx, /* Start x */
|
||||
sy, /* Start y */
|
||||
ex, /* End x */
|
||||
ey; /* End y */
|
||||
} PVRTileClipCommand; /* Tile Clip command for the pvr */
|
||||
|
||||
typedef struct {
|
||||
unsigned int list_type;
|
||||
AlignedVector vector;
|
||||
} PolyList;
|
||||
|
||||
typedef struct {
|
||||
GLint x;
|
||||
GLint y;
|
||||
GLint width;
|
||||
GLint height;
|
||||
|
||||
float x_plus_hwidth;
|
||||
float y_plus_hheight;
|
||||
float hwidth; /* width * 0.5f */
|
||||
float hheight; /* height * 0.5f */
|
||||
} Viewport;
|
||||
|
||||
extern Viewport VIEWPORT;
|
||||
|
||||
typedef struct {
|
||||
//0
|
||||
GLuint index;
|
||||
GLuint color; /* This is the PVR texture format */
|
||||
//8
|
||||
GLenum minFilter;
|
||||
GLenum magFilter;
|
||||
//16
|
||||
GLvoid *data;
|
||||
//20
|
||||
GLushort width;
|
||||
GLushort height;
|
||||
//24
|
||||
GLushort mipmap; /* Bitmask of supplied mipmap levels */
|
||||
// 26
|
||||
GLushort _pad0;
|
||||
// 28
|
||||
GLubyte mipmap_bias;
|
||||
GLubyte env;
|
||||
GLubyte _pad1;
|
||||
GLubyte _pad2;
|
||||
//32
|
||||
GLubyte padding[32]; // Pad to 64-bytes
|
||||
} __attribute__((aligned(32))) TextureObject;
|
||||
|
||||
|
||||
#define argbcpy(dst, src) \
|
||||
*((GLuint*) dst) = *((const GLuint*) src) \
|
||||
|
||||
|
||||
typedef struct {
|
||||
float xy[2];
|
||||
} _glvec2;
|
||||
|
||||
typedef struct {
|
||||
float xyz[3];
|
||||
} _glvec3;
|
||||
|
||||
typedef struct {
|
||||
float xyzw[4];
|
||||
} _glvec4;
|
||||
|
||||
#define vec2cpy(dst, src) \
|
||||
*((_glvec2*) dst) = *((_glvec2*) src)
|
||||
|
||||
#define vec3cpy(dst, src) \
|
||||
*((_glvec3*) dst) = *((_glvec3*) src)
|
||||
|
||||
#define vec4cpy(dst, src) \
|
||||
*((_glvec4*) dst) = *((_glvec4*) src)
|
||||
|
||||
GL_FORCE_INLINE float clamp(float d, float min, float max) {
|
||||
return (d < min) ? min : (d > max) ? max : d;
|
||||
}
|
||||
|
||||
GL_FORCE_INLINE void memcpy_vertex(Vertex *dest, const Vertex *src) {
|
||||
#ifdef __DREAMCAST__
|
||||
_Complex float double_scratch;
|
||||
|
||||
asm volatile (
|
||||
"fschg\n\t"
|
||||
"clrs\n\t"
|
||||
".align 2\n\t"
|
||||
"fmov.d @%[in]+, %[scratch]\n\t"
|
||||
"fmov.d %[scratch], @%[out]\n\t"
|
||||
"fmov.d @%[in]+, %[scratch]\n\t"
|
||||
"add #8, %[out]\n\t"
|
||||
"fmov.d %[scratch], @%[out]\n\t"
|
||||
"fmov.d @%[in]+, %[scratch]\n\t"
|
||||
"add #8, %[out]\n\t"
|
||||
"fmov.d %[scratch], @%[out]\n\t"
|
||||
"fmov.d @%[in], %[scratch]\n\t"
|
||||
"add #8, %[out]\n\t"
|
||||
"fmov.d %[scratch], @%[out]\n\t"
|
||||
"fschg\n"
|
||||
: [in] "+&r" ((uint32_t) src), [scratch] "=&d" (double_scratch), [out] "+&r" ((uint32_t) dest)
|
||||
:
|
||||
: "t", "memory" // clobbers
|
||||
);
|
||||
#else
|
||||
*dest = *src;
|
||||
#endif
|
||||
}
|
||||
|
||||
#define swapVertex(a, b) \
|
||||
do { \
|
||||
Vertex __attribute__((aligned(32))) c; \
|
||||
memcpy_vertex(&c, a); \
|
||||
memcpy_vertex(a, b); \
|
||||
memcpy_vertex(b, &c); \
|
||||
} while(0)
|
||||
|
||||
/* Generating PVR vertices from the user-submitted data gets complicated, particularly
|
||||
* when a realloc could invalidate pointers. This structure holds all the information
|
||||
* we need on the target vertex array to allow passing around to the various stages (e.g. generate/clip etc.)
|
||||
*/
|
||||
typedef struct __attribute__((aligned(32))) {
|
||||
PolyList* output;
|
||||
uint32_t header_offset; // The offset of the header in the output list
|
||||
uint32_t start_offset; // The offset into the output list
|
||||
uint32_t count; // The number of vertices in this output
|
||||
} SubmissionTarget;
|
||||
|
||||
Vertex* _glSubmissionTargetStart(SubmissionTarget* target);
|
||||
Vertex* _glSubmissionTargetEnd(SubmissionTarget* target);
|
||||
|
||||
typedef enum {
|
||||
CLIP_RESULT_ALL_IN_FRONT,
|
||||
CLIP_RESULT_ALL_BEHIND,
|
||||
CLIP_RESULT_ALL_ON_PLANE,
|
||||
CLIP_RESULT_FRONT_TO_BACK,
|
||||
CLIP_RESULT_BACK_TO_FRONT
|
||||
} ClipResult;
|
||||
|
||||
|
||||
#define A8IDX 3
|
||||
#define R8IDX 2
|
||||
#define G8IDX 1
|
||||
#define B8IDX 0
|
||||
|
||||
struct SubmissionTarget;
|
||||
|
||||
PolyList* _glOpaquePolyList();
|
||||
PolyList* _glPunchThruPolyList();
|
||||
PolyList *_glTransparentPolyList();
|
||||
|
||||
void _glInitAttributePointers();
|
||||
void _glInitContext();
|
||||
void _glInitMatrices();
|
||||
void _glInitSubmissionTarget();
|
||||
|
||||
void _glMatrixLoadModelViewProjection();
|
||||
|
||||
GLubyte _glInitTextures();
|
||||
|
||||
void _glUpdatePVRTextureContext(PolyContext* context, GLshort textureUnit);
|
||||
|
||||
typedef struct {
|
||||
const void* ptr; // 4
|
||||
GLenum type; // 4
|
||||
GLsizei stride; // 4
|
||||
GLint size; // 4
|
||||
} AttribPointer;
|
||||
|
||||
typedef struct {
|
||||
AttribPointer vertex; // 16
|
||||
AttribPointer colour; // 32
|
||||
AttribPointer uv; // 48
|
||||
AttribPointer st; // 64
|
||||
AttribPointer normal; // 80
|
||||
AttribPointer padding; // 96
|
||||
} AttribPointerList;
|
||||
|
||||
GLboolean _glCheckValidEnum(GLint param, GLint* values, const char* func);
|
||||
|
||||
GLenum _glGetShadeModel();
|
||||
|
||||
extern TextureObject* TEXTURE_ACTIVE;
|
||||
extern GLboolean TEXTURES_ENABLED;
|
||||
|
||||
GLboolean _glIsBlendingEnabled();
|
||||
GLboolean _glIsAlphaTestEnabled();
|
||||
GLboolean _glIsCullingEnabled();
|
||||
GLboolean _glIsDepthTestEnabled();
|
||||
GLboolean _glIsDepthWriteEnabled();
|
||||
GLboolean _glIsScissorTestEnabled();
|
||||
GLboolean _glIsFogEnabled();
|
||||
GLenum _glGetDepthFunc();
|
||||
GLenum _glGetCullFace();
|
||||
GLenum _glGetFrontFace();
|
||||
GLenum _glGetBlendSourceFactor();
|
||||
GLenum _glGetBlendDestFactor();
|
||||
|
||||
extern PolyList OP_LIST;
|
||||
extern PolyList PT_LIST;
|
||||
extern PolyList TR_LIST;
|
||||
|
||||
GL_FORCE_INLINE PolyList* _glActivePolyList() {
|
||||
if(_glIsBlendingEnabled()) {
|
||||
return &TR_LIST;
|
||||
} else if(_glIsAlphaTestEnabled()) {
|
||||
return &PT_LIST;
|
||||
} else {
|
||||
return &OP_LIST;
|
||||
}
|
||||
}
|
||||
|
||||
extern GLenum LAST_ERROR;
|
||||
extern char ERROR_FUNCTION[64];
|
||||
|
||||
GL_FORCE_INLINE const char* _glErrorEnumAsString(GLenum error) {
|
||||
switch(error) {
|
||||
case GL_INVALID_ENUM: return "GL_INVALID_ENUM";
|
||||
case GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY";
|
||||
case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION";
|
||||
case GL_INVALID_VALUE: return "GL_INVALID_VALUE";
|
||||
default:
|
||||
return "GL_UNKNOWN_ERROR";
|
||||
}
|
||||
}
|
||||
|
||||
GL_FORCE_INLINE void _glKosThrowError(GLenum error, const char *function) {
|
||||
if(LAST_ERROR == GL_NO_ERROR) {
|
||||
LAST_ERROR = error;
|
||||
sprintf(ERROR_FUNCTION, "%s\n", function);
|
||||
fprintf(stderr, "GL ERROR: %s when calling %s\n", _glErrorEnumAsString(LAST_ERROR), ERROR_FUNCTION);
|
||||
}
|
||||
}
|
||||
|
||||
GL_FORCE_INLINE GLubyte _glKosHasError() {
|
||||
return (LAST_ERROR != GL_NO_ERROR) ? GL_TRUE : GL_FALSE;
|
||||
}
|
||||
|
||||
GL_FORCE_INLINE void _glKosResetError() {
|
||||
LAST_ERROR = GL_NO_ERROR;
|
||||
sprintf(ERROR_FUNCTION, "\n");
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
float n[3]; // 12 bytes
|
||||
float finalColour[4]; //28 bytes
|
||||
uint32_t padding; // 32 bytes
|
||||
} EyeSpaceData;
|
||||
|
||||
unsigned char _glIsClippingEnabled();
|
||||
void _glEnableClipping(unsigned char v);
|
||||
|
||||
GLuint _glFreeTextureMemory();
|
||||
GLuint _glUsedTextureMemory();
|
||||
GLuint _glFreeContiguousTextureMemory();
|
||||
|
||||
void _glApplyScissor(bool force);
|
||||
|
||||
GLboolean _glNearZClippingEnabled();
|
||||
|
||||
GLboolean _glGPUStateIsDirty();
|
||||
void _glGPUStateMarkClean();
|
||||
void _glGPUStateMarkDirty();
|
||||
|
||||
#define MAX_GLDC_TEXTURE_UNITS 2
|
||||
|
||||
|
||||
/* This is from KOS pvr_buffers.c */
|
||||
#define PVR_MIN_Z 0.0001f
|
||||
|
||||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||||
#define MAX(a,b) (((a)>(b))?(a):(b))
|
||||
#define CLAMP( X, _MIN, _MAX ) ( (X)<(_MIN) ? (_MIN) : ((X)>(_MAX) ? (_MAX) : (X)) )
|
||||
|
||||
#endif // PRIVATE_H
|
470
third_party/gldc/src/sh4.c
vendored
Normal file
470
third_party/gldc/src/sh4.c
vendored
Normal file
@ -0,0 +1,470 @@
|
||||
#include "platform.h"
|
||||
#include "sh4.h"
|
||||
|
||||
|
||||
#define CLIP_DEBUG 0
|
||||
|
||||
#define PVR_VERTEX_BUF_SIZE 2560 * 256
|
||||
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
|
||||
#define SQ_BASE_ADDRESS (void*) 0xe0000000
|
||||
|
||||
|
||||
GL_FORCE_INLINE bool glIsVertex(const float flags) {
|
||||
return flags == GPU_CMD_VERTEX_EOL || flags == GPU_CMD_VERTEX;
|
||||
}
|
||||
|
||||
GL_FORCE_INLINE bool glIsLastVertex(const float flags) {
|
||||
return flags == GPU_CMD_VERTEX_EOL;
|
||||
}
|
||||
|
||||
void InitGPU(_Bool autosort, _Bool fsaa) {
|
||||
pvr_init_params_t params = {
|
||||
/* Enable opaque and translucent polygons with size 32 and 32 */
|
||||
{PVR_BINSIZE_32, PVR_BINSIZE_0, PVR_BINSIZE_32, PVR_BINSIZE_0, PVR_BINSIZE_32},
|
||||
PVR_VERTEX_BUF_SIZE, /* Vertex buffer size */
|
||||
0, /* No DMA */
|
||||
fsaa, /* No FSAA */
|
||||
(autosort) ? 0 : 1 /* Disable translucent auto-sorting to match traditional GL */
|
||||
};
|
||||
|
||||
pvr_init(¶ms);
|
||||
|
||||
/* If we're PAL and we're NOT VGA, then use 50hz by default. This is the safest
|
||||
thing to do. If someone wants to force 60hz then they can call vid_set_mode later and hopefully
|
||||
that'll work... */
|
||||
|
||||
int cable = vid_check_cable();
|
||||
int region = flashrom_get_region();
|
||||
|
||||
if(region == FLASHROM_REGION_EUROPE && cable != CT_VGA) {
|
||||
printf("PAL region without VGA - enabling 50hz");
|
||||
vid_set_mode(DM_640x480_PAL_IL, PM_RGB565);
|
||||
}
|
||||
}
|
||||
|
||||
void SceneBegin() {
|
||||
pvr_wait_ready();
|
||||
pvr_scene_begin();
|
||||
}
|
||||
|
||||
void SceneListBegin(GPUList list) {
|
||||
pvr_list_begin(list);
|
||||
}
|
||||
|
||||
GL_FORCE_INLINE float _glFastInvert(float x) {
|
||||
return (1.f / __builtin_sqrtf(x * x));
|
||||
}
|
||||
|
||||
GL_FORCE_INLINE void _glPerspectiveDivideVertex(Vertex* vertex, const float h) {
|
||||
TRACE();
|
||||
|
||||
const float f = _glFastInvert(vertex->w);
|
||||
|
||||
/* Convert to NDC and apply viewport */
|
||||
vertex->xyz[0] = (vertex->xyz[0] * f * 320) + 320;
|
||||
vertex->xyz[1] = (vertex->xyz[1] * f * -240) + 240;
|
||||
|
||||
/* Orthographic projections need to use invZ otherwise we lose
|
||||
the depth information. As w == 1, and clip-space range is -w to +w
|
||||
we add 1.0 to the Z to bring it into range. We add a little extra to
|
||||
avoid a divide by zero.
|
||||
*/
|
||||
if(vertex->w == 1.0f) {
|
||||
vertex->xyz[2] = _glFastInvert(1.0001f + vertex->xyz[2]);
|
||||
} else {
|
||||
vertex->xyz[2] = f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
volatile uint32_t *sq = SQ_BASE_ADDRESS;
|
||||
|
||||
static inline void _glFlushBuffer() {
|
||||
TRACE();
|
||||
|
||||
/* Wait for both store queues to complete */
|
||||
sq = (uint32_t*) 0xe0000000;
|
||||
sq[0] = sq[8] = 0;
|
||||
}
|
||||
|
||||
static inline void _glPushHeaderOrVertex(Vertex* v) {
|
||||
TRACE();
|
||||
|
||||
uint32_t* s = (uint32_t*) v;
|
||||
sq[0] = *(s++);
|
||||
sq[1] = *(s++);
|
||||
sq[2] = *(s++);
|
||||
sq[3] = *(s++);
|
||||
sq[4] = *(s++);
|
||||
sq[5] = *(s++);
|
||||
sq[6] = *(s++);
|
||||
sq[7] = *(s++);
|
||||
__asm__("pref @%0" : : "r"(sq));
|
||||
sq += 8;
|
||||
}
|
||||
|
||||
static inline void _glClipEdge(const Vertex* const v1, const Vertex* const v2, Vertex* vout) {
|
||||
const static float o = 0.003921569f; // 1 / 255
|
||||
const float d0 = v1->w + v1->xyz[2];
|
||||
const float d1 = v2->w + v2->xyz[2];
|
||||
const float t = (fabs(d0) * (1.0f / sqrtf((d1 - d0) * (d1 - d0)))) + 0.000001f;
|
||||
const float invt = 1.0f - t;
|
||||
|
||||
vout->xyz[0] = invt * v1->xyz[0] + t * v2->xyz[0];
|
||||
vout->xyz[1] = invt * v1->xyz[1] + t * v2->xyz[1];
|
||||
vout->xyz[2] = invt * v1->xyz[2] + t * v2->xyz[2];
|
||||
|
||||
vout->uv[0] = invt * v1->uv[0] + t * v2->uv[0];
|
||||
vout->uv[1] = invt * v1->uv[1] + t * v2->uv[1];
|
||||
|
||||
vout->w = invt * v1->w + t * v2->w;
|
||||
|
||||
const float m = 255 * t;
|
||||
const float n = 255 - m;
|
||||
|
||||
vout->bgra[0] = (v1->bgra[0] * n + v2->bgra[0] * m) * o;
|
||||
vout->bgra[1] = (v1->bgra[1] * n + v2->bgra[1] * m) * o;
|
||||
vout->bgra[2] = (v1->bgra[2] * n + v2->bgra[2] * m) * o;
|
||||
vout->bgra[3] = (v1->bgra[3] * n + v2->bgra[3] * m) * o;
|
||||
}
|
||||
|
||||
#define SPAN_SORT_CFG 0x005F8030
|
||||
static volatile uint32_t* PVR_LMMODE0 = (uint32_t*) 0xA05F6884;
|
||||
static volatile uint32_t *PVR_LMMODE1 = (uint32_t*) 0xA05F6888;
|
||||
static volatile uint32_t *QACR = (uint32_t*) 0xFF000038;
|
||||
|
||||
void SceneListSubmit(Vertex* v2, int n) {
|
||||
TRACE();
|
||||
|
||||
/* You need at least a header, and 3 vertices to render anything */
|
||||
if(n < 4) {
|
||||
return;
|
||||
}
|
||||
|
||||
const float h = GetVideoMode()->height;
|
||||
|
||||
PVR_SET(SPAN_SORT_CFG, 0x0);
|
||||
|
||||
//Set PVR DMA registers
|
||||
*PVR_LMMODE0 = 0;
|
||||
*PVR_LMMODE1 = 0;
|
||||
|
||||
//Set QACR registers
|
||||
QACR[1] = QACR[0] = 0x11;
|
||||
|
||||
#if CLIP_DEBUG
|
||||
Vertex* vertex = (Vertex*) src;
|
||||
for(int i = 0; i < n; ++i) {
|
||||
fprintf(stderr, "{%f, %f, %f, %f}, // %x (%x)\n", vertex[i].xyz[0], vertex[i].xyz[1], vertex[i].xyz[2], vertex[i].w, vertex[i].flags, &vertex[i]);
|
||||
}
|
||||
|
||||
fprintf(stderr, "----\n");
|
||||
#endif
|
||||
uint8_t visible_mask = 0;
|
||||
uint8_t counter = 0;
|
||||
|
||||
sq = SQ_BASE_ADDRESS;
|
||||
|
||||
for(int i = 0; i < n; ++i, ++v2) {
|
||||
PREFETCH(v2 + 1);
|
||||
switch(v2->flags) {
|
||||
case GPU_CMD_VERTEX_EOL:
|
||||
if(counter < 2) {
|
||||
continue;
|
||||
}
|
||||
counter = 0;
|
||||
break;
|
||||
case GPU_CMD_VERTEX:
|
||||
++counter;
|
||||
if(counter < 3) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
_glPushHeaderOrVertex(v2);
|
||||
counter = 0;
|
||||
continue;
|
||||
};
|
||||
|
||||
Vertex* const v0 = v2 - 2;
|
||||
Vertex* const v1 = v2 - 1;
|
||||
|
||||
visible_mask = (
|
||||
(v0->xyz[2] > -v0->w) << 0 |
|
||||
(v1->xyz[2] > -v1->w) << 1 |
|
||||
(v2->xyz[2] > -v2->w) << 2 |
|
||||
(counter == 0) << 3
|
||||
);
|
||||
|
||||
switch(visible_mask) {
|
||||
case 15: /* All visible, but final vertex in strip */
|
||||
{
|
||||
_glPerspectiveDivideVertex(v0, h);
|
||||
_glPushHeaderOrVertex(v0);
|
||||
|
||||
_glPerspectiveDivideVertex(v1, h);
|
||||
_glPushHeaderOrVertex(v1);
|
||||
|
||||
_glPerspectiveDivideVertex(v2, h);
|
||||
_glPushHeaderOrVertex(v2);
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
/* All visible, push the first vertex and move on */
|
||||
_glPerspectiveDivideVertex(v0, h);
|
||||
_glPushHeaderOrVertex(v0);
|
||||
break;
|
||||
case 9:
|
||||
/* First vertex was visible, last in strip */
|
||||
{
|
||||
Vertex __attribute__((aligned(32))) scratch[2];
|
||||
Vertex* a = &scratch[0];
|
||||
Vertex* b = &scratch[1];
|
||||
|
||||
_glClipEdge(v0, v1, a);
|
||||
a->flags = GPU_CMD_VERTEX;
|
||||
|
||||
_glClipEdge(v2, v0, b);
|
||||
b->flags = GPU_CMD_VERTEX_EOL;
|
||||
|
||||
_glPerspectiveDivideVertex(v0, h);
|
||||
_glPushHeaderOrVertex(v0);
|
||||
|
||||
_glPerspectiveDivideVertex(a, h);
|
||||
_glPushHeaderOrVertex(a);
|
||||
|
||||
_glPerspectiveDivideVertex(b, h);
|
||||
_glPushHeaderOrVertex(b);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
/* First vertex was visible, but not last in strip */
|
||||
{
|
||||
Vertex __attribute__((aligned(32))) scratch[2];
|
||||
Vertex* a = &scratch[0];
|
||||
Vertex* b = &scratch[1];
|
||||
|
||||
_glClipEdge(v0, v1, a);
|
||||
a->flags = GPU_CMD_VERTEX;
|
||||
|
||||
_glClipEdge(v2, v0, b);
|
||||
b->flags = GPU_CMD_VERTEX;
|
||||
|
||||
_glPerspectiveDivideVertex(v0, h);
|
||||
_glPushHeaderOrVertex(v0);
|
||||
|
||||
_glPerspectiveDivideVertex(a, h);
|
||||
_glPushHeaderOrVertex(a);
|
||||
|
||||
_glPerspectiveDivideVertex(b, h);
|
||||
_glPushHeaderOrVertex(b);
|
||||
_glPushHeaderOrVertex(b);
|
||||
}
|
||||
break;
|
||||
case 10:
|
||||
case 2:
|
||||
/* Second vertex was visible. In self case we need to create a triangle and produce
|
||||
two new vertices: 1-2, and 2-3. */
|
||||
{
|
||||
Vertex __attribute__((aligned(32))) scratch[3];
|
||||
Vertex* a = &scratch[0];
|
||||
Vertex* b = &scratch[1];
|
||||
Vertex* c = &scratch[2];
|
||||
|
||||
memcpy_vertex(c, v1);
|
||||
|
||||
_glClipEdge(v0, v1, a);
|
||||
a->flags = GPU_CMD_VERTEX;
|
||||
|
||||
_glClipEdge(v1, v2, b);
|
||||
b->flags = v2->flags;
|
||||
|
||||
_glPerspectiveDivideVertex(a, h);
|
||||
_glPushHeaderOrVertex(a);
|
||||
|
||||
_glPerspectiveDivideVertex(c, h);
|
||||
_glPushHeaderOrVertex(c);
|
||||
|
||||
_glPerspectiveDivideVertex(b, h);
|
||||
_glPushHeaderOrVertex(b);
|
||||
}
|
||||
break;
|
||||
case 11:
|
||||
case 3: /* First and second vertex were visible */
|
||||
{
|
||||
Vertex __attribute__((aligned(32))) scratch[3];
|
||||
Vertex* a = &scratch[0];
|
||||
Vertex* b = &scratch[1];
|
||||
Vertex* c = &scratch[2];
|
||||
|
||||
memcpy_vertex(c, v1);
|
||||
|
||||
_glClipEdge(v2, v0, b);
|
||||
b->flags = GPU_CMD_VERTEX;
|
||||
|
||||
_glPerspectiveDivideVertex(v0, h);
|
||||
_glPushHeaderOrVertex(v0);
|
||||
|
||||
_glClipEdge(v1, v2, a);
|
||||
a->flags = v2->flags;
|
||||
|
||||
_glPerspectiveDivideVertex(c, h);
|
||||
_glPushHeaderOrVertex(c);
|
||||
|
||||
_glPerspectiveDivideVertex(b, h);
|
||||
_glPushHeaderOrVertex(b);
|
||||
|
||||
_glPerspectiveDivideVertex(a, h);
|
||||
_glPushHeaderOrVertex(c);
|
||||
_glPushHeaderOrVertex(a);
|
||||
}
|
||||
break;
|
||||
case 12:
|
||||
case 4:
|
||||
/* Third vertex was visible. */
|
||||
{
|
||||
Vertex __attribute__((aligned(32))) scratch[3];
|
||||
Vertex* a = &scratch[0];
|
||||
Vertex* b = &scratch[1];
|
||||
Vertex* c = &scratch[2];
|
||||
|
||||
memcpy_vertex(c, v2);
|
||||
|
||||
_glClipEdge(v2, v0, a);
|
||||
a->flags = GPU_CMD_VERTEX;
|
||||
|
||||
_glClipEdge(v1, v2, b);
|
||||
b->flags = GPU_CMD_VERTEX;
|
||||
|
||||
_glPerspectiveDivideVertex(a, h);
|
||||
_glPushHeaderOrVertex(a);
|
||||
|
||||
if(counter % 2 == 1) {
|
||||
_glPushHeaderOrVertex(a);
|
||||
}
|
||||
|
||||
_glPerspectiveDivideVertex(b, h);
|
||||
_glPushHeaderOrVertex(b);
|
||||
|
||||
_glPerspectiveDivideVertex(c, h);
|
||||
_glPushHeaderOrVertex(c);
|
||||
}
|
||||
break;
|
||||
case 13:
|
||||
{
|
||||
Vertex __attribute__((aligned(32))) scratch[3];
|
||||
Vertex* a = &scratch[0];
|
||||
Vertex* b = &scratch[1];
|
||||
Vertex* c = &scratch[2];
|
||||
|
||||
memcpy_vertex(c, v2);
|
||||
c->flags = GPU_CMD_VERTEX;
|
||||
|
||||
_glClipEdge(v0, v1, a);
|
||||
a->flags = GPU_CMD_VERTEX;
|
||||
|
||||
_glClipEdge(v1, v2, b);
|
||||
b->flags = GPU_CMD_VERTEX;
|
||||
|
||||
_glPerspectiveDivideVertex(v0, h);
|
||||
_glPushHeaderOrVertex(v0);
|
||||
|
||||
_glPerspectiveDivideVertex(a, h);
|
||||
_glPushHeaderOrVertex(a);
|
||||
|
||||
_glPerspectiveDivideVertex(c, h);
|
||||
_glPushHeaderOrVertex(c);
|
||||
_glPerspectiveDivideVertex(b, h);
|
||||
_glPushHeaderOrVertex(b);
|
||||
|
||||
c->flags = GPU_CMD_VERTEX_EOL;
|
||||
_glPushHeaderOrVertex(c);
|
||||
}
|
||||
break;
|
||||
case 5: /* First and third vertex were visible */
|
||||
{
|
||||
Vertex __attribute__((aligned(32))) scratch[3];
|
||||
Vertex* a = &scratch[0];
|
||||
Vertex* b = &scratch[1];
|
||||
Vertex* c = &scratch[2];
|
||||
|
||||
memcpy_vertex(c, v2);
|
||||
c->flags = GPU_CMD_VERTEX;
|
||||
|
||||
_glClipEdge(v0, v1, a);
|
||||
a->flags = GPU_CMD_VERTEX;
|
||||
|
||||
_glClipEdge(v1, v2, b);
|
||||
b->flags = GPU_CMD_VERTEX;
|
||||
|
||||
_glPerspectiveDivideVertex(v0, h);
|
||||
_glPushHeaderOrVertex(v0);
|
||||
|
||||
_glPerspectiveDivideVertex(a, h);
|
||||
_glPushHeaderOrVertex(a);
|
||||
|
||||
_glPerspectiveDivideVertex(c, h);
|
||||
_glPushHeaderOrVertex(c);
|
||||
_glPerspectiveDivideVertex(b, h);
|
||||
_glPushHeaderOrVertex(b);
|
||||
_glPushHeaderOrVertex(c);
|
||||
}
|
||||
break;
|
||||
case 14:
|
||||
case 6: /* Second and third vertex were visible */
|
||||
{
|
||||
Vertex __attribute__((aligned(32))) scratch[4];
|
||||
Vertex* a = &scratch[0];
|
||||
Vertex* b = &scratch[1];
|
||||
Vertex* c = &scratch[2];
|
||||
Vertex* d = &scratch[3];
|
||||
|
||||
memcpy_vertex(c, v1);
|
||||
memcpy_vertex(d, v2);
|
||||
|
||||
_glClipEdge(v0, v1, a);
|
||||
a->flags = GPU_CMD_VERTEX;
|
||||
|
||||
_glClipEdge(v2, v0, b);
|
||||
b->flags = GPU_CMD_VERTEX;
|
||||
|
||||
_glPerspectiveDivideVertex(a, h);
|
||||
_glPushHeaderOrVertex(a);
|
||||
|
||||
_glPerspectiveDivideVertex(c, h);
|
||||
_glPushHeaderOrVertex(c);
|
||||
|
||||
_glPerspectiveDivideVertex(b, h);
|
||||
_glPushHeaderOrVertex(b);
|
||||
_glPushHeaderOrVertex(c);
|
||||
|
||||
_glPerspectiveDivideVertex(d, h);
|
||||
_glPushHeaderOrVertex(d);
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_glFlushBuffer();
|
||||
}
|
||||
|
||||
void SceneListFinish() {
|
||||
pvr_list_finish();
|
||||
}
|
||||
|
||||
void SceneFinish() {
|
||||
pvr_scene_finish();
|
||||
}
|
||||
|
||||
const VideoMode* GetVideoMode() {
|
||||
static VideoMode mode;
|
||||
mode.width = vid_mode->width;
|
||||
mode.height = vid_mode->height;
|
||||
return &mode;
|
||||
}
|
119
third_party/gldc/src/sh4.h
vendored
Normal file
119
third_party/gldc/src/sh4.h
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
#pragma once
|
||||
|
||||
#include <kos.h>
|
||||
#include <dc/matrix.h>
|
||||
#include <dc/pvr.h>
|
||||
#include <dc/vec3f.h>
|
||||
#include <dc/fmath.h>
|
||||
#include <dc/matrix3d.h>
|
||||
|
||||
#include "types.h"
|
||||
#include "private.h"
|
||||
|
||||
#include "sh4_math.h"
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define PERF_WARNING(msg) printf("[PERF] %s\n", msg)
|
||||
#else
|
||||
#define PERF_WARNING(msg) (void) 0
|
||||
#endif
|
||||
|
||||
#ifndef GL_FORCE_INLINE
|
||||
#define GL_NO_INSTRUMENT inline __attribute__((no_instrument_function))
|
||||
#define GL_INLINE_DEBUG GL_NO_INSTRUMENT __attribute__((always_inline))
|
||||
#define GL_FORCE_INLINE static GL_INLINE_DEBUG
|
||||
#endif
|
||||
|
||||
#define PREFETCH(addr) __builtin_prefetch((addr))
|
||||
|
||||
GL_FORCE_INLINE void* memcpy_fast(void *dest, const void *src, size_t len) {
|
||||
if(!len) {
|
||||
return dest;
|
||||
}
|
||||
|
||||
const uint8_t *s = (uint8_t *)src;
|
||||
uint8_t *d = (uint8_t *)dest;
|
||||
|
||||
uint32_t diff = (uint32_t)d - (uint32_t)(s + 1); // extra offset because input gets incremented before output is calculated
|
||||
// Underflow would be like adding a negative offset
|
||||
|
||||
// Can use 'd' as a scratch reg now
|
||||
asm volatile (
|
||||
"clrs\n" // Align for parallelism (CO) - SH4a use "stc SR, Rn" instead with a dummy Rn
|
||||
".align 2\n"
|
||||
"0:\n\t"
|
||||
"dt %[size]\n\t" // (--len) ? 0 -> T : 1 -> T (EX 1)
|
||||
"mov.b @%[in]+, %[scratch]\n\t" // scratch = *(s++) (LS 1/2)
|
||||
"bf.s 0b\n\t" // while(s != nexts) aka while(!T) (BR 1/2)
|
||||
" mov.b %[scratch], @(%[offset], %[in])\n" // *(datatype_of_s*) ((char*)s + diff) = scratch, where src + diff = dest (LS 1)
|
||||
: [in] "+&r" ((uint32_t)s), [scratch] "=&r" ((uint32_t)d), [size] "+&r" (len) // outputs
|
||||
: [offset] "z" (diff) // inputs
|
||||
: "t", "memory" // clobbers
|
||||
);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
/* We use sq_cpy if the src and size is properly aligned. We control that the
|
||||
* destination is properly aligned so we assert that. */
|
||||
#define FASTCPY(dst, src, bytes) \
|
||||
do { \
|
||||
if(bytes % 32 == 0 && ((uintptr_t) src % 4) == 0) { \
|
||||
gl_assert(((uintptr_t) dst) % 32 == 0); \
|
||||
sq_cpy(dst, src, bytes); \
|
||||
} else { \
|
||||
memcpy_fast(dst, src, bytes); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
#define MEMCPY4(dst, src, bytes) memcpy_fast(dst, src, bytes)
|
||||
|
||||
#define MEMSET4(dst, v, size) memset4((dst), (v), (size))
|
||||
|
||||
#define VEC3_NORMALIZE(x, y, z) vec3f_normalize((x), (y), (z))
|
||||
#define VEC3_LENGTH(x, y, z, l) vec3f_length((x), (y), (z), (l))
|
||||
#define VEC3_DOT(x1, y1, z1, x2, y2, z2, d) vec3f_dot((x1), (y1), (z1), (x2), (y2), (z2), (d))
|
||||
|
||||
GL_FORCE_INLINE void TransformVertex(const float* xyz, const float* w, float* oxyz, float* ow) {
|
||||
register float __x __asm__("fr12") = (xyz[0]);
|
||||
register float __y __asm__("fr13") = (xyz[1]);
|
||||
register float __z __asm__("fr14") = (xyz[2]);
|
||||
register float __w __asm__("fr15") = (*w);
|
||||
|
||||
__asm__ __volatile__(
|
||||
"fldi1 fr15\n"
|
||||
"ftrv xmtrx,fv12\n"
|
||||
: "=f" (__x), "=f" (__y), "=f" (__z), "=f" (__w)
|
||||
: "0" (__x), "1" (__y), "2" (__z), "3" (__w)
|
||||
);
|
||||
|
||||
oxyz[0] = __x;
|
||||
oxyz[1] = __y;
|
||||
oxyz[2] = __z;
|
||||
*ow = __w;
|
||||
}
|
||||
|
||||
void InitGPU(_Bool autosort, _Bool fsaa);
|
||||
|
||||
static inline size_t GPUMemoryAvailable() {
|
||||
return pvr_mem_available();
|
||||
}
|
||||
|
||||
static inline void* GPUMemoryAlloc(size_t size) {
|
||||
return pvr_mem_malloc(size);
|
||||
}
|
||||
|
||||
static inline void GPUSetBackgroundColour(float r, float g, float b) {
|
||||
pvr_set_bg_color(r, g, b);
|
||||
}
|
||||
|
||||
#define PT_ALPHA_REF 0x011c
|
||||
|
||||
static inline void GPUSetAlphaCutOff(uint8_t val) {
|
||||
PVR_SET(PT_ALPHA_REF, val);
|
||||
}
|
||||
|
||||
static inline void GPUSetClearDepth(float v) {
|
||||
pvr_set_zclip(v);
|
||||
}
|
1820
third_party/gldc/src/sh4_math.h
vendored
Normal file
1820
third_party/gldc/src/sh4_math.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
499
third_party/gldc/src/state.c
vendored
Normal file
499
third_party/gldc/src/state.c
vendored
Normal file
@ -0,0 +1,499 @@
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "private.h"
|
||||
|
||||
|
||||
static struct {
|
||||
GLboolean is_dirty;
|
||||
|
||||
/* We can't just use the GL_CONTEXT for this state as the two
|
||||
* GL states are combined, so we store them separately and then
|
||||
* calculate the appropriate PVR state from them. */
|
||||
GLenum depth_func;
|
||||
GLboolean depth_test_enabled;
|
||||
GLenum cull_face;
|
||||
GLenum front_face;
|
||||
GLboolean culling_enabled;
|
||||
GLboolean znear_clipping_enabled;
|
||||
GLboolean alpha_test_enabled;
|
||||
GLboolean scissor_test_enabled;
|
||||
GLboolean fog_enabled;
|
||||
GLboolean depth_mask_enabled;
|
||||
|
||||
struct {
|
||||
GLint x;
|
||||
GLint y;
|
||||
GLsizei width;
|
||||
GLsizei height;
|
||||
GLboolean applied;
|
||||
} scissor_rect;
|
||||
|
||||
GLenum blend_sfactor;
|
||||
GLenum blend_dfactor;
|
||||
GLboolean blend_enabled;
|
||||
|
||||
GLenum shade_model;
|
||||
} GPUState = {
|
||||
.is_dirty = GL_TRUE,
|
||||
.depth_func = GL_LESS,
|
||||
.depth_test_enabled = GL_FALSE,
|
||||
.cull_face = GL_BACK,
|
||||
.front_face = GL_CCW,
|
||||
.culling_enabled = GL_FALSE,
|
||||
.znear_clipping_enabled = GL_TRUE,
|
||||
.alpha_test_enabled = GL_FALSE,
|
||||
.scissor_test_enabled = GL_FALSE,
|
||||
.fog_enabled = GL_FALSE,
|
||||
.depth_mask_enabled = GL_FALSE,
|
||||
.scissor_rect = {0, 0, 640, 480, false},
|
||||
.blend_sfactor = GL_ONE,
|
||||
.blend_dfactor = GL_ZERO,
|
||||
.blend_enabled = GL_FALSE,
|
||||
.shade_model = GL_SMOOTH
|
||||
};
|
||||
|
||||
void _glGPUStateMarkClean() {
|
||||
GPUState.is_dirty = GL_FALSE;
|
||||
}
|
||||
|
||||
void _glGPUStateMarkDirty() {
|
||||
GPUState.is_dirty = GL_TRUE;
|
||||
}
|
||||
|
||||
GLboolean _glGPUStateIsDirty() {
|
||||
return GPUState.is_dirty;
|
||||
}
|
||||
|
||||
GLboolean _glIsDepthTestEnabled() {
|
||||
return GPUState.depth_test_enabled;
|
||||
}
|
||||
|
||||
GLenum _glGetDepthFunc() {
|
||||
return GPUState.depth_func;
|
||||
}
|
||||
|
||||
GLboolean _glIsDepthWriteEnabled() {
|
||||
return GPUState.depth_mask_enabled;
|
||||
}
|
||||
|
||||
GLenum _glGetShadeModel() {
|
||||
return GPUState.shade_model;
|
||||
}
|
||||
|
||||
GLboolean _glIsBlendingEnabled() {
|
||||
return GPUState.blend_enabled;
|
||||
}
|
||||
|
||||
GLboolean _glIsAlphaTestEnabled() {
|
||||
return GPUState.alpha_test_enabled;
|
||||
}
|
||||
|
||||
GLboolean _glIsCullingEnabled() {
|
||||
return GPUState.culling_enabled;
|
||||
}
|
||||
|
||||
GLenum _glGetCullFace() {
|
||||
return GPUState.cull_face;
|
||||
}
|
||||
|
||||
GLenum _glGetFrontFace() {
|
||||
return GPUState.front_face;
|
||||
}
|
||||
|
||||
GLboolean _glIsFogEnabled() {
|
||||
return GPUState.fog_enabled;
|
||||
}
|
||||
|
||||
GLboolean _glIsScissorTestEnabled() {
|
||||
return GPUState.scissor_test_enabled;
|
||||
}
|
||||
|
||||
GLboolean _glNearZClippingEnabled() {
|
||||
return GPUState.znear_clipping_enabled;
|
||||
}
|
||||
|
||||
void _glApplyScissor(bool force);
|
||||
|
||||
GLenum _glGetBlendSourceFactor() {
|
||||
return GPUState.blend_sfactor;
|
||||
}
|
||||
|
||||
GLenum _glGetBlendDestFactor() {
|
||||
return GPUState.blend_dfactor;
|
||||
}
|
||||
|
||||
|
||||
GLboolean _glCheckValidEnum(GLint param, GLint* values, const char* func) {
|
||||
GLubyte found = 0;
|
||||
while(*values != 0) {
|
||||
if(*values == param) {
|
||||
found++;
|
||||
break;
|
||||
}
|
||||
values++;
|
||||
}
|
||||
|
||||
if(!found) {
|
||||
_glKosThrowError(GL_INVALID_ENUM, func);
|
||||
return GL_TRUE;
|
||||
}
|
||||
|
||||
return GL_FALSE;
|
||||
}
|
||||
|
||||
GLboolean TEXTURES_ENABLED = GL_FALSE;
|
||||
|
||||
void _glUpdatePVRTextureContext(PolyContext *context, GLshort textureUnit) {
|
||||
const TextureObject *tx1 = TEXTURE_ACTIVE;
|
||||
|
||||
/* Disable all texturing to start with */
|
||||
context->txr.enable = GPU_TEXTURE_DISABLE;
|
||||
context->txr2.enable = GPU_TEXTURE_DISABLE;
|
||||
context->txr2.alpha = GPU_TXRALPHA_DISABLE;
|
||||
|
||||
if(!TEXTURES_ENABLED || !tx1 || !tx1->data) {
|
||||
context->txr.base = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
context->txr.alpha = (GPUState.blend_enabled || GPUState.alpha_test_enabled) ? GPU_TXRALPHA_ENABLE : GPU_TXRALPHA_DISABLE;
|
||||
|
||||
GLuint filter = GPU_FILTER_NEAREST;
|
||||
|
||||
if(tx1->minFilter == GL_LINEAR && tx1->magFilter == GL_LINEAR) {
|
||||
filter = GPU_FILTER_BILINEAR;
|
||||
}
|
||||
|
||||
if(tx1->data) {
|
||||
context->txr.enable = GPU_TEXTURE_ENABLE;
|
||||
context->txr.filter = filter;
|
||||
context->txr.width = tx1->width;
|
||||
context->txr.height = tx1->height;
|
||||
context->txr.mipmap = GL_FALSE;
|
||||
context->txr.mipmap_bias = tx1->mipmap_bias;
|
||||
|
||||
context->txr.base = tx1->data;
|
||||
context->txr.format = tx1->color;
|
||||
context->txr.env = tx1->env;
|
||||
context->txr.uv_flip = GPU_UVFLIP_NONE;
|
||||
context->txr.uv_clamp = GPU_UVCLAMP_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
void _glInitContext() {
|
||||
const VideoMode* mode = GetVideoMode();
|
||||
|
||||
GPUState.scissor_rect.x = 0;
|
||||
GPUState.scissor_rect.y = 0;
|
||||
GPUState.scissor_rect.width = mode->width;
|
||||
GPUState.scissor_rect.height = mode->height;
|
||||
|
||||
glClearDepth(1.0f);
|
||||
glDepthFunc(GL_LESS);
|
||||
glDepthMask(GL_TRUE);
|
||||
glFrontFace(GL_CCW);
|
||||
glCullFace(GL_BACK);
|
||||
glShadeModel(GL_SMOOTH);
|
||||
|
||||
glDisable(GL_ALPHA_TEST);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glDisable(GL_FOG);
|
||||
}
|
||||
|
||||
GLAPI void APIENTRY glEnable(GLenum cap) {
|
||||
switch(cap) {
|
||||
case GL_TEXTURE_2D:
|
||||
if(TEXTURES_ENABLED != GL_TRUE) {
|
||||
TEXTURES_ENABLED = GL_TRUE;
|
||||
GPUState.is_dirty = GL_TRUE;
|
||||
}
|
||||
break;
|
||||
case GL_CULL_FACE: {
|
||||
if(GPUState.culling_enabled != GL_TRUE) {
|
||||
GPUState.culling_enabled = GL_TRUE;
|
||||
GPUState.is_dirty = GL_TRUE;
|
||||
}
|
||||
|
||||
} break;
|
||||
case GL_DEPTH_TEST: {
|
||||
if(GPUState.depth_test_enabled != GL_TRUE) {
|
||||
GPUState.depth_test_enabled = GL_TRUE;
|
||||
GPUState.is_dirty = GL_TRUE;
|
||||
}
|
||||
} break;
|
||||
case GL_BLEND: {
|
||||
if(GPUState.blend_enabled != GL_TRUE) {
|
||||
GPUState.blend_enabled = GL_TRUE;
|
||||
GPUState.is_dirty = GL_TRUE;
|
||||
}
|
||||
} break;
|
||||
case GL_SCISSOR_TEST: {
|
||||
if(GPUState.scissor_test_enabled != GL_TRUE) {
|
||||
GPUState.scissor_test_enabled = GL_TRUE;
|
||||
GPUState.is_dirty = GL_TRUE;
|
||||
}
|
||||
} break;
|
||||
case GL_FOG:
|
||||
if(GPUState.fog_enabled != GL_TRUE) {
|
||||
GPUState.fog_enabled = GL_TRUE;
|
||||
GPUState.is_dirty = GL_TRUE;
|
||||
}
|
||||
break;
|
||||
case GL_ALPHA_TEST: {
|
||||
if(GPUState.alpha_test_enabled != GL_TRUE) {
|
||||
GPUState.alpha_test_enabled = GL_TRUE;
|
||||
GPUState.is_dirty = GL_TRUE;
|
||||
}
|
||||
} break;
|
||||
case GL_NEARZ_CLIPPING_KOS:
|
||||
if(GPUState.znear_clipping_enabled != GL_TRUE) {
|
||||
GPUState.znear_clipping_enabled = GL_TRUE;
|
||||
GPUState.is_dirty = GL_TRUE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
GLAPI void APIENTRY glDisable(GLenum cap) {
|
||||
switch(cap) {
|
||||
case GL_TEXTURE_2D:
|
||||
if(TEXTURES_ENABLED != GL_FALSE) {
|
||||
TEXTURES_ENABLED = GL_FALSE;
|
||||
GPUState.is_dirty = GL_TRUE;
|
||||
}
|
||||
break;
|
||||
case GL_CULL_FACE: {
|
||||
if(GPUState.culling_enabled != GL_FALSE) {
|
||||
GPUState.culling_enabled = GL_FALSE;
|
||||
GPUState.is_dirty = GL_TRUE;
|
||||
}
|
||||
|
||||
} break;
|
||||
case GL_DEPTH_TEST: {
|
||||
if(GPUState.depth_test_enabled != GL_FALSE) {
|
||||
GPUState.depth_test_enabled = GL_FALSE;
|
||||
GPUState.is_dirty = GL_TRUE;
|
||||
}
|
||||
} break;
|
||||
case GL_BLEND: {
|
||||
if(GPUState.blend_enabled != GL_FALSE) {
|
||||
GPUState.blend_enabled = GL_FALSE;
|
||||
GPUState.is_dirty = GL_TRUE;
|
||||
}
|
||||
} break;
|
||||
case GL_SCISSOR_TEST: {
|
||||
if(GPUState.scissor_test_enabled != GL_FALSE) {
|
||||
GPUState.scissor_test_enabled = GL_FALSE;
|
||||
GPUState.is_dirty = GL_TRUE;
|
||||
}
|
||||
} break;
|
||||
case GL_FOG:
|
||||
if(GPUState.fog_enabled != GL_FALSE) {
|
||||
GPUState.fog_enabled = GL_FALSE;
|
||||
GPUState.is_dirty = GL_TRUE;
|
||||
}
|
||||
break;
|
||||
case GL_ALPHA_TEST: {
|
||||
if(GPUState.alpha_test_enabled != GL_FALSE) {
|
||||
GPUState.alpha_test_enabled = GL_FALSE;
|
||||
GPUState.is_dirty = GL_TRUE;
|
||||
}
|
||||
} break;
|
||||
case GL_NEARZ_CLIPPING_KOS:
|
||||
if(GPUState.znear_clipping_enabled != GL_FALSE) {
|
||||
GPUState.znear_clipping_enabled = GL_FALSE;
|
||||
GPUState.is_dirty = GL_TRUE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Depth Testing */
|
||||
GLAPI void APIENTRY glClearDepthf(GLfloat depth) {
|
||||
glClearDepth(depth);
|
||||
}
|
||||
|
||||
GLAPI void APIENTRY glClearDepth(GLfloat depth) {
|
||||
/* We reverse because using invW means that farther Z == lower number */
|
||||
GPUSetClearDepth(MIN(1.0f - depth, PVR_MIN_Z));
|
||||
}
|
||||
|
||||
GLAPI void APIENTRY glDepthMask(GLboolean flag) {
|
||||
if(GPUState.depth_mask_enabled != flag) {
|
||||
GPUState.depth_mask_enabled = flag;
|
||||
GPUState.is_dirty = GL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
GLAPI void APIENTRY glDepthFunc(GLenum func) {
|
||||
if(GPUState.depth_func != func) {
|
||||
GPUState.depth_func = func;
|
||||
GPUState.is_dirty = GL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Culling */
|
||||
GLAPI void APIENTRY glFrontFace(GLenum mode) {
|
||||
if(GPUState.front_face != mode) {
|
||||
GPUState.front_face = mode;
|
||||
GPUState.is_dirty = GL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
GLAPI void APIENTRY glCullFace(GLenum mode) {
|
||||
if(GPUState.cull_face != mode) {
|
||||
GPUState.cull_face = mode;
|
||||
GPUState.is_dirty = GL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Shading - Flat or Goraud */
|
||||
GLAPI void APIENTRY glShadeModel(GLenum mode) {
|
||||
if(GPUState.shade_model != mode) {
|
||||
GPUState.shade_model = mode;
|
||||
GPUState.is_dirty = GL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Blending */
|
||||
GLAPI void APIENTRY glBlendFunc(GLenum sfactor, GLenum dfactor) {
|
||||
if(GPUState.blend_dfactor != dfactor || GPUState.blend_sfactor != sfactor) {
|
||||
GPUState.blend_sfactor = sfactor;
|
||||
GPUState.blend_dfactor = dfactor;
|
||||
GPUState.is_dirty = GL_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GLAPI void APIENTRY glAlphaFunc(GLenum func, GLclampf ref) {
|
||||
GLubyte val = (GLubyte)(ref * 255.0f);
|
||||
GPUSetAlphaCutOff(val);
|
||||
}
|
||||
|
||||
void APIENTRY glScissor(GLint x, GLint y, GLsizei width, GLsizei height) {
|
||||
|
||||
if(GPUState.scissor_rect.x == x &&
|
||||
GPUState.scissor_rect.y == y &&
|
||||
GPUState.scissor_rect.width == width &&
|
||||
GPUState.scissor_rect.height == height) {
|
||||
return;
|
||||
}
|
||||
|
||||
GPUState.scissor_rect.x = x;
|
||||
GPUState.scissor_rect.y = y;
|
||||
GPUState.scissor_rect.width = width;
|
||||
GPUState.scissor_rect.height = height;
|
||||
GPUState.scissor_rect.applied = false;
|
||||
GPUState.is_dirty = GL_TRUE; // FIXME: do we need this?
|
||||
|
||||
_glApplyScissor(false);
|
||||
}
|
||||
|
||||
/* Setup the hardware user clip rectangle.
|
||||
|
||||
The minimum clip rectangle is a 32x32 area which is dependent on the tile
|
||||
size use by the tile accelerator. The PVR swithes off rendering to tiles
|
||||
outside or inside the defined rectangle dependant upon the 'clipmode'
|
||||
bits in the polygon header.
|
||||
|
||||
Clip rectangles therefore must have a size that is some multiple of 32.
|
||||
|
||||
glScissor(0, 0, 32, 32) allows only the 'tile' in the lower left
|
||||
hand corner of the screen to be modified and glScissor(0, 0, 0, 0)
|
||||
disallows modification to all 'tiles' on the screen.
|
||||
|
||||
We call this in the following situations:
|
||||
|
||||
- glEnable(GL_SCISSOR_TEST) is called
|
||||
- glScissor() is called
|
||||
- After glKosSwapBuffers()
|
||||
|
||||
This ensures that a clip command is added to every vertex list
|
||||
at the right place, either when enabling the scissor test, or
|
||||
when the scissor test changes.
|
||||
*/
|
||||
void _glApplyScissor(bool force) {
|
||||
/* Don't do anyting if clipping is disabled */
|
||||
if(!GPUState.scissor_test_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Don't apply if we already applied - nothing changed */
|
||||
if(GPUState.scissor_rect.applied && !force) {
|
||||
return;
|
||||
}
|
||||
|
||||
PVRTileClipCommand c;
|
||||
|
||||
GLint miny, maxx, maxy;
|
||||
|
||||
const VideoMode* vid_mode = GetVideoMode();
|
||||
|
||||
GLsizei scissor_width = MAX(MIN(GPUState.scissor_rect.width, vid_mode->width), 0);
|
||||
GLsizei scissor_height = MAX(MIN(GPUState.scissor_rect.height, vid_mode->height), 0);
|
||||
|
||||
/* force the origin to the lower left-hand corner of the screen */
|
||||
miny = (vid_mode->height - scissor_height) - GPUState.scissor_rect.y;
|
||||
maxx = (scissor_width + GPUState.scissor_rect.x);
|
||||
maxy = (scissor_height + miny);
|
||||
|
||||
/* load command structure while mapping screen coords to TA tiles */
|
||||
c.flags = GPU_CMD_USERCLIP;
|
||||
c.d1 = c.d2 = c.d3 = 0;
|
||||
|
||||
uint16_t vw = vid_mode->width >> 5;
|
||||
uint16_t vh = vid_mode->height >> 5;
|
||||
|
||||
c.sx = CLAMP(GPUState.scissor_rect.x >> 5, 0, vw);
|
||||
c.sy = CLAMP(miny >> 5, 0, vh);
|
||||
c.ex = CLAMP((maxx >> 5) - 1, 0, vw);
|
||||
c.ey = CLAMP((maxy >> 5) - 1, 0, vh);
|
||||
|
||||
aligned_vector_push_back(&_glOpaquePolyList()->vector, &c, 1);
|
||||
aligned_vector_push_back(&_glPunchThruPolyList()->vector, &c, 1);
|
||||
aligned_vector_push_back(&_glTransparentPolyList()->vector, &c, 1);
|
||||
|
||||
GPUState.scissor_rect.applied = true;
|
||||
}
|
||||
|
||||
void APIENTRY glGetIntegerv(GLenum pname, GLint *params) {
|
||||
switch(pname) {
|
||||
case GL_MAX_TEXTURE_SIZE:
|
||||
*params = MAX_TEXTURE_SIZE;
|
||||
break;
|
||||
case GL_TEXTURE_FREE_MEMORY_ATI:
|
||||
case GL_FREE_TEXTURE_MEMORY_KOS:
|
||||
*params = _glFreeTextureMemory();
|
||||
break;
|
||||
case GL_USED_TEXTURE_MEMORY_KOS:
|
||||
*params = _glUsedTextureMemory();
|
||||
break;
|
||||
case GL_FREE_CONTIGUOUS_TEXTURE_MEMORY_KOS:
|
||||
*params = _glFreeContiguousTextureMemory();
|
||||
break;
|
||||
default:
|
||||
_glKosThrowError(GL_INVALID_ENUM, __func__);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const GLubyte *glGetString(GLenum name) {
|
||||
switch(name) {
|
||||
case GL_VENDOR:
|
||||
return (const GLubyte*) "KallistiOS / Kazade";
|
||||
|
||||
case GL_RENDERER:
|
||||
return (const GLubyte*) "PowerVR2 CLX2 100mHz";
|
||||
}
|
||||
|
||||
return (const GLubyte*) "GL_KOS_ERROR: ENUM Unsupported\n";
|
||||
}
|
280
third_party/gldc/src/texture.c
vendored
Normal file
280
third_party/gldc/src/texture.c
vendored
Normal file
@ -0,0 +1,280 @@
|
||||
#include "private.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
#include "yalloc/yalloc.h"
|
||||
|
||||
/* We always leave this amount of vram unallocated to prevent
|
||||
* issues with the allocator */
|
||||
#define PVR_MEM_BUFFER_SIZE (64 * 1024)
|
||||
|
||||
TextureObject* TEXTURE_ACTIVE = NULL;
|
||||
static NamedArray TEXTURE_OBJECTS;
|
||||
|
||||
static void* YALLOC_BASE = NULL;
|
||||
static size_t YALLOC_SIZE = 0;
|
||||
|
||||
static void* yalloc_alloc_and_defrag(size_t size) {
|
||||
void* ret = yalloc_alloc(YALLOC_BASE, size);
|
||||
|
||||
if(!ret) {
|
||||
/* Tried to allocate, but out of room, let's try defragging
|
||||
* and repeating the alloc */
|
||||
fprintf(stderr, "Ran out of memory, defragmenting\n");
|
||||
glDefragmentTextureMemory_KOS();
|
||||
ret = yalloc_alloc(YALLOC_BASE, size);
|
||||
}
|
||||
|
||||
gl_assert(ret && "Out of PVR memory!");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define GL_KOS_INTERNAL_DEFAULT_MIPMAP_LOD_BIAS 4
|
||||
static void _glInitializeTextureObject(TextureObject* txr, unsigned int id) {
|
||||
txr->index = id;
|
||||
txr->width = txr->height = 0;
|
||||
txr->mipmap = 0;
|
||||
txr->env = GPU_TXRENV_MODULATEALPHA;
|
||||
txr->data = NULL;
|
||||
txr->minFilter = GL_NEAREST;
|
||||
txr->magFilter = GL_NEAREST;
|
||||
txr->mipmap_bias = GL_KOS_INTERNAL_DEFAULT_MIPMAP_LOD_BIAS;
|
||||
}
|
||||
|
||||
GLubyte _glInitTextures() {
|
||||
named_array_init(&TEXTURE_OBJECTS, sizeof(TextureObject), MAX_TEXTURE_COUNT);
|
||||
|
||||
// Reserve zero so that it is never given to anyone as an ID!
|
||||
named_array_reserve(&TEXTURE_OBJECTS, 0);
|
||||
|
||||
// Initialize zero as an actual texture object though because apparently it is!
|
||||
TextureObject* default_tex = (TextureObject*) named_array_get(&TEXTURE_OBJECTS, 0);
|
||||
_glInitializeTextureObject(default_tex, 0);
|
||||
TEXTURE_ACTIVE = default_tex;
|
||||
|
||||
size_t vram_free = GPUMemoryAvailable();
|
||||
YALLOC_SIZE = vram_free - PVR_MEM_BUFFER_SIZE; /* Take all but 64kb VRAM */
|
||||
YALLOC_BASE = GPUMemoryAlloc(YALLOC_SIZE);
|
||||
|
||||
#ifdef __DREAMCAST__
|
||||
/* Ensure memory is aligned */
|
||||
gl_assert((uintptr_t) YALLOC_BASE % 32 == 0);
|
||||
#endif
|
||||
|
||||
yalloc_init(YALLOC_BASE, YALLOC_SIZE);
|
||||
|
||||
gl_assert(TEXTURE_OBJECTS.element_size > 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
GLuint APIENTRY gldcGenTexture(void) {
|
||||
TRACE();
|
||||
|
||||
gl_assert(TEXTURE_OBJECTS.element_size > 0);
|
||||
|
||||
GLuint id = 0;
|
||||
TextureObject* txr = (TextureObject*) named_array_alloc(&TEXTURE_OBJECTS, &id);
|
||||
gl_assert(txr);
|
||||
gl_assert(id); // Generated IDs must never be zero
|
||||
|
||||
_glInitializeTextureObject(txr, id);
|
||||
|
||||
gl_assert(txr->index == id);
|
||||
gl_assert(TEXTURE_OBJECTS.element_size > 0);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void APIENTRY gldcDeleteTexture(GLuint id) {
|
||||
TRACE();
|
||||
|
||||
gl_assert(TEXTURE_OBJECTS.element_size > 0);
|
||||
|
||||
if(id == 0) {
|
||||
/* Zero is the "default texture" and we never allow deletion of it */
|
||||
return;
|
||||
}
|
||||
|
||||
TextureObject* txr = (TextureObject*) named_array_get(&TEXTURE_OBJECTS, id);
|
||||
|
||||
if(txr) {
|
||||
gl_assert(txr->index == id);
|
||||
|
||||
if(txr == TEXTURE_ACTIVE) {
|
||||
// Reset to the default texture
|
||||
TEXTURE_ACTIVE = (TextureObject*) named_array_get(&TEXTURE_OBJECTS, 0);
|
||||
}
|
||||
|
||||
if(txr->data) {
|
||||
yalloc_free(YALLOC_BASE, txr->data);
|
||||
txr->data = NULL;
|
||||
}
|
||||
|
||||
named_array_release(&TEXTURE_OBJECTS, id);
|
||||
}
|
||||
|
||||
gl_assert(TEXTURE_OBJECTS.element_size > 0);
|
||||
}
|
||||
|
||||
void APIENTRY gldcBindTexture(GLuint id) {
|
||||
TRACE();
|
||||
|
||||
TextureObject* txr = (TextureObject*) named_array_get(&TEXTURE_OBJECTS, id);
|
||||
|
||||
TEXTURE_ACTIVE = txr;
|
||||
gl_assert(TEXTURE_ACTIVE->index == id);
|
||||
|
||||
gl_assert(TEXTURE_OBJECTS.element_size > 0);
|
||||
|
||||
_glGPUStateMarkDirty();
|
||||
}
|
||||
|
||||
static GLuint _determinePVRFormat(GLint internalFormat, GLenum type) {
|
||||
/* Given a cleaned internalFormat, return the Dreamcast format
|
||||
* that can hold it
|
||||
*/
|
||||
switch(internalFormat) {
|
||||
case GL_RGBA:
|
||||
/* OK so if we have something that requires alpha, we return 4444 unless
|
||||
* the type was already 1555 (1-bit alpha) in which case we return that
|
||||
*/
|
||||
if(type == GL_UNSIGNED_SHORT_1_5_5_5_REV) {
|
||||
return GPU_TXRFMT_ARGB1555 | GPU_TXRFMT_NONTWIDDLED;
|
||||
} else if(type == GL_UNSIGNED_SHORT_1_5_5_5_REV_TWID_KOS) {
|
||||
return GPU_TXRFMT_ARGB1555 | GPU_TXRFMT_TWIDDLED;
|
||||
} else if(type == GL_UNSIGNED_SHORT_4_4_4_4_REV_TWID_KOS) {
|
||||
return GPU_TXRFMT_ARGB4444 | GPU_TXRFMT_TWIDDLED;
|
||||
} else {
|
||||
return GPU_TXRFMT_ARGB4444 | GPU_TXRFMT_NONTWIDDLED;
|
||||
}
|
||||
/* Compressed and twiddled versions */
|
||||
case GL_UNSIGNED_SHORT_5_6_5_TWID_KOS:
|
||||
return GPU_TXRFMT_RGB565 | GPU_TXRFMT_TWIDDLED;
|
||||
case GL_UNSIGNED_SHORT_4_4_4_4_REV_TWID_KOS:
|
||||
return GPU_TXRFMT_ARGB4444 | GPU_TXRFMT_TWIDDLED;
|
||||
case GL_UNSIGNED_SHORT_1_5_5_5_REV_TWID_KOS:
|
||||
return GPU_TXRFMT_ARGB1555 | GPU_TXRFMT_TWIDDLED;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define STRINGIFY(x) #x
|
||||
#define TOSTRING(x) STRINGIFY(x)
|
||||
#define INFO_MSG(x) fprintf(stderr, "%s:%s > %s\n", __FILE__, TOSTRING(__LINE__), x)
|
||||
|
||||
void APIENTRY gldcAllocTexture(GLsizei w, GLsizei h, GLenum format, GLenum type) {
|
||||
TRACE();
|
||||
|
||||
if (w > 1024 || h > 1024){
|
||||
INFO_MSG("Invalid texture size");
|
||||
_glKosThrowError(GL_INVALID_VALUE, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if((w < 8 || (w & -w) != w)) {
|
||||
/* Width is not a power of two. Must be!*/
|
||||
INFO_MSG("");
|
||||
_glKosThrowError(GL_INVALID_VALUE, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if((h < 8 || (h & -h) != h)) {
|
||||
/* height is not a power of two. Must be!*/
|
||||
INFO_MSG("");
|
||||
_glKosThrowError(GL_INVALID_VALUE, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
TextureObject* active = TEXTURE_ACTIVE;
|
||||
/* Calculate the format that we need to convert the data to */
|
||||
GLuint pvr_format = _determinePVRFormat(format, type);
|
||||
|
||||
if(active->data) {
|
||||
/* pre-existing texture - check if changed */
|
||||
if(active->width != w || active->height != h) {
|
||||
/* changed - free old texture memory */
|
||||
yalloc_free(YALLOC_BASE, active->data);
|
||||
active->data = NULL;
|
||||
active->mipmap = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* All colour formats are represented as shorts internally. */
|
||||
GLuint bytes = w * h * 2;
|
||||
|
||||
if(!active->data) {
|
||||
/* need texture memory */
|
||||
active->width = w;
|
||||
active->height = h;
|
||||
active->color = pvr_format;
|
||||
|
||||
active->data = yalloc_alloc_and_defrag(bytes);
|
||||
}
|
||||
|
||||
gl_assert(active->data);
|
||||
|
||||
/* If we run out of PVR memory just return */
|
||||
if(!active->data) {
|
||||
_glKosThrowError(GL_OUT_OF_MEMORY, __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Mark level 0 as set in the mipmap bitmask */
|
||||
active->mipmap |= (1 << 0);
|
||||
|
||||
/* We make sure we remove nontwiddled and add twiddled. We could always
|
||||
* make it twiddled when determining the format but I worry that would make the
|
||||
* code less flexible to change in the future */
|
||||
active->color &= ~(1 << 26);
|
||||
|
||||
_glGPUStateMarkDirty();
|
||||
}
|
||||
|
||||
GLAPI void APIENTRY gldcGetTexture(GLvoid** data, GLsizei* width, GLsizei* height) {
|
||||
TextureObject* active = TEXTURE_ACTIVE;
|
||||
*data = active->data;
|
||||
*width = active->width;
|
||||
*height = active->height;
|
||||
}
|
||||
|
||||
GLuint _glMaxTextureMemory() {
|
||||
return YALLOC_SIZE;
|
||||
}
|
||||
|
||||
GLuint _glFreeTextureMemory() {
|
||||
return yalloc_count_free(YALLOC_BASE);
|
||||
}
|
||||
|
||||
GLuint _glUsedTextureMemory() {
|
||||
return YALLOC_SIZE - _glFreeTextureMemory();
|
||||
}
|
||||
|
||||
GLuint _glFreeContiguousTextureMemory() {
|
||||
return yalloc_count_continuous(YALLOC_BASE);
|
||||
}
|
||||
|
||||
GLAPI GLvoid APIENTRY glDefragmentTextureMemory_KOS(void) {
|
||||
yalloc_defrag_start(YALLOC_BASE);
|
||||
|
||||
GLuint id;
|
||||
|
||||
/* Replace all texture pointers */
|
||||
for(id = 0; id < MAX_TEXTURE_COUNT; id++){
|
||||
TextureObject* txr = (TextureObject*) named_array_get(&TEXTURE_OBJECTS, id);
|
||||
if(txr){
|
||||
gl_assert(txr->index == id);
|
||||
txr->data = yalloc_defrag_address(YALLOC_BASE, txr->data);
|
||||
}
|
||||
}
|
||||
|
||||
yalloc_defrag_commit(YALLOC_BASE);
|
||||
}
|
||||
|
16
third_party/gldc/src/types.h
vendored
Normal file
16
third_party/gldc/src/types.h
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
/* Same 32 byte layout as pvr_vertex_t */
|
||||
uint32_t flags;
|
||||
float xyz[3];
|
||||
float uv[2];
|
||||
uint8_t bgra[4];
|
||||
|
||||
/* In the pvr_vertex_t structure, this next 4 bytes is oargb
|
||||
* but we're not using that for now, so having W here makes the code
|
||||
* simpler */
|
||||
float w;
|
||||
} __attribute__ ((aligned (32))) Vertex;
|
21
third_party/gldc/src/yalloc/LICENSE
vendored
Normal file
21
third_party/gldc/src/yalloc/LICENSE
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) [year] [fullname]
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
158
third_party/gldc/src/yalloc/README.md
vendored
Normal file
158
third_party/gldc/src/yalloc/README.md
vendored
Normal file
@ -0,0 +1,158 @@
|
||||
# Summary
|
||||
|
||||
yalloc is a memory efficient allocator which is intended for embedded
|
||||
applications that only have a low amount of RAM and want to maximize its
|
||||
utilization. Properties of the allocator:
|
||||
|
||||
- pools can be up to 128k
|
||||
- user data is 32bit aligned
|
||||
- 4 bytes overhead per allocation
|
||||
- supports defragmentation
|
||||
- uses a free list for first fit allocation strategy (most recently freed
|
||||
blocks are used first)
|
||||
- extensively tested (see section below)
|
||||
- MIT license
|
||||
|
||||
# Defragmentation
|
||||
|
||||
This feature was the initial motivation for this implementation. Especially
|
||||
when dealing with highly memory constrained environments fragmenting memory
|
||||
pools can be annoying. For this reason this implementation supports
|
||||
defragmentation which moves all allocated blocks into a contiguous range at the
|
||||
beginning of the pool, leaving a maximized free range at the end.
|
||||
|
||||
As there is no garbage collector or other runtime system involved that updates
|
||||
the references, the application must do so. This is done in three steps:
|
||||
|
||||
1. yalloc_defrag_start() is called. This calculates the new
|
||||
post-defragmentation-addresses for all allocations, but otherwise leaves
|
||||
the allocations untouched.
|
||||
|
||||
2. yalloc_defrag_address() is called by the application for every pointer that
|
||||
points to an allocation. It returns the post-defragmentation-address for
|
||||
the allocation. The application must update all its relevant pointers this
|
||||
way. Care must be taken not not yet dereference that moved pointers. If the
|
||||
application works with hierarchical data then this can easily be done by
|
||||
updating the pointers button up (first the leafs then their parents).
|
||||
|
||||
3. yalloc_defrag_commit() is called to finally perform the defragmentation.
|
||||
All allocated blocks are moved to their post-defragmentation-address and
|
||||
the application can continue using the pool the normal way.
|
||||
|
||||
It is up to the application when (and if) it performs defragmentation. One
|
||||
strategy would be to delay it until an allocation failure. Another approach
|
||||
would be to perform the defragmentation regularly when there is nothing else to
|
||||
do.
|
||||
|
||||
# Configurable Defines
|
||||
|
||||
INTERNAL_VALIDATE
|
||||
|
||||
If this is not defined on the compiler commandline it will be defined as 0 if
|
||||
NDEBUG is defined and otherwise as 1. If you want to disable internal
|
||||
validation when NDEBUG is not defined then define INERNAL_VALIDATE as 0 on the
|
||||
compiler commandline.
|
||||
|
||||
If it is nonzero the heap will be validated via a bunch of assert() calls at
|
||||
the end of every function that modifies the heap. This has roughly O(N*M)
|
||||
overhead where N is the number of allocated blocks and M the number of free
|
||||
blocks in a heap. For applications with enough live allocations this will get
|
||||
significant.
|
||||
|
||||
YALLOC_VALGRIND
|
||||
|
||||
If this is defined in yalloc.c and NVALGRIND is not defined then
|
||||
valgrind/memcheck.h is included and the the allocator functions tell valgrind
|
||||
about the pool, the allocations and makes the block headers inaccessible outside
|
||||
of yalloc-functions. This allows valgrind to detect a lot of the accidents that
|
||||
can happen when dealing dynamic memory. This also adds some overhead for every
|
||||
yalloc-call because most of them will "unprotect" the internal structure on
|
||||
entry and "protect" it again (marking it as inaccessible for valgrind) before
|
||||
returning.
|
||||
|
||||
# Tests
|
||||
|
||||
The tests rely on internal validation of the pool (see INTERNAL_VALIDATE) to
|
||||
check that no assumptions about the internal structure of the pool are
|
||||
violated. They additionally check for correctness of observations that can be
|
||||
made by using the public functions of the allocator (like checking if user data
|
||||
stays unmodified). There are a few different scripts that run tests:
|
||||
|
||||
- run_coverage.sh runs a bunch of testfunctions that are carefully crafted to
|
||||
cover all code paths. Coverage data is generated by clang and a summary is
|
||||
shown at the end of the test.
|
||||
|
||||
- run_valgrind.sh tests if the valgrind integration is working as expected,
|
||||
runs the functions from the coverage test and some randomly generated
|
||||
testcases under valgrind.
|
||||
|
||||
- run_libfuzzer.sh uses libfuzzer from clang to generate interesting testcases
|
||||
and runs them in multiple jobs in parallel for 10 seconds. It also generates
|
||||
coverage data at the end (it always got 100% coverage in my testruns).
|
||||
|
||||
All tests exit with 0 and print "All fine!" at the end if there where no
|
||||
errors. Coverage deficits are not counted as error, so you have to look at the
|
||||
summary (they should show 100% coverage!).
|
||||
|
||||
|
||||
# Implementation Details
|
||||
|
||||
The Headers and the user data are 32bit aligned. Headers have two 16bit fields
|
||||
where the high 15 bits represent offsets (relative to the pools address) to the
|
||||
previous/next block. The macros HDR_PTR() and HDR_OFFSET() are used to
|
||||
translate an offset to an address and back. The 32bit alignment is exploited to
|
||||
allow pools of up to 128k with that 15 significant bits.
|
||||
|
||||
A pool is always occupied by non-overlapping blocks that link to their
|
||||
previous/next block in address order via the prev/next field of Header.
|
||||
|
||||
Free blocks are always joined: No two free blocks will ever be neighbors.
|
||||
|
||||
Free blocks have an additional header of the same structure. This additional
|
||||
header is used to build a list of free blocks (independent of their address
|
||||
order).
|
||||
|
||||
yalloc_free() will insert the freed block to the front of the free list.
|
||||
yalloc_alloc() searches that list front to back and takes the first block that
|
||||
is big enough to satisfy the allocation.
|
||||
|
||||
There is always a Header at the front and at the end of the pool. The Header at
|
||||
the end is degenerate: It is marked as "used" but has no next block (which is
|
||||
usually used to determine the size of a block).
|
||||
|
||||
The prev-field of the very first block in the pool has special meaning: It
|
||||
points to the first free block in the pool. Or, if the pool is currently
|
||||
defragmenting (after yalloc_defrag_start() and before yalloc_defrag_commit()),
|
||||
points to the last header of the pool. This state can be recognized by checking
|
||||
if it points to an empty block (normal pool state) or a used block
|
||||
(defragmentation in progress). This logic can be seen in
|
||||
yalloc_defrag_in_progress().
|
||||
|
||||
The lowest bit of next/prev have special meaning:
|
||||
|
||||
- low bit of prev is set for free blocks
|
||||
|
||||
- low bit of next is set for blocks with 32bit padding after the user data.
|
||||
This is needed when a block is allocated from a free block that leaves only
|
||||
4 free bytes after the user data... which is not enough to insert a
|
||||
free-header (which is needs 8 bytes). The padding will be reclaimed when
|
||||
that block is freed or when the pool is defragmented. The predicate
|
||||
isPadded() can be used to test if a block is padded. Free blocks are never
|
||||
padded.
|
||||
|
||||
The predicate isNil() can be used to test if an offset points nowhere (it tests
|
||||
if all 15 high bits of an offset are 1). The constant NIL has all but the
|
||||
lowest bit set. It is used to set offsets to point to nowhere, and in some
|
||||
places it is used to mask out the actual address bits of an offset. This should
|
||||
be kept in mind when modifying the code and updating prev/next: Think carefully
|
||||
if you have to preserve the low bit when updating an offset!
|
||||
|
||||
Defragmentation is done in two phases: First the user calls
|
||||
yalloc_defrag_start(). This will put the pool in a special state where no
|
||||
alloc/free-calls are allowed. In this state the prev-fields of the used blocks
|
||||
have a special meaning: They store the offset that the block will have after
|
||||
defragmentation finished. This information is used by yalloc_defrag_address()
|
||||
which can be called by the application to query the new addresses for its
|
||||
allocations. After the application has updated all its pointers it must call
|
||||
yalloc_defrag_commit() which moves all used blocks in contiguous space at the
|
||||
beginning of the pool, leaving one maximized free block at the end.
|
803
third_party/gldc/src/yalloc/yalloc.c
vendored
Normal file
803
third_party/gldc/src/yalloc/yalloc.c
vendored
Normal file
@ -0,0 +1,803 @@
|
||||
#include "yalloc.h"
|
||||
#include "yalloc_internals.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#define ALIGN(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
|
||||
|
||||
#if defined(YALLOC_VALGRIND) && !defined(NVALGRIND)
|
||||
# define USE_VALGRIND 1
|
||||
#else
|
||||
# define USE_VALGRIND 0
|
||||
#endif
|
||||
|
||||
#if USE_VALGRIND
|
||||
# include <valgrind/memcheck.h>
|
||||
#else
|
||||
# define VALGRIND_MAKE_MEM_UNDEFINED(p, s) ((void)0)
|
||||
# define VALGRIND_MAKE_MEM_DEFINED(p, s) ((void)0)
|
||||
# define VALGRIND_MAKE_MEM_NOACCESS(p, s) ((void)0)
|
||||
# define VALGRIND_CREATE_MEMPOOL(pool, rz, z) ((void)0)
|
||||
# define VALGRIND_MEMPOOL_ALLOC(pool, p, s) ((void)0)
|
||||
# define VALGRIND_MEMPOOL_FREE(pool, p) ((void)0)
|
||||
# define VALGRIND_MEMPOOL_CHANGE(pool, a, b, s) ((void)0)
|
||||
#endif
|
||||
|
||||
#define MARK_NEW_FREE_HDR(p) VALGRIND_MAKE_MEM_UNDEFINED(p, sizeof(Header) * 2)
|
||||
#define MARK_NEW_HDR(p) VALGRIND_MAKE_MEM_UNDEFINED(p, sizeof(Header))
|
||||
#define PROTECT_HDR(p) VALGRIND_MAKE_MEM_NOACCESS(p, sizeof(Header))
|
||||
#define PROTECT_FREE_HDR(p) VALGRIND_MAKE_MEM_NOACCESS(p, sizeof(Header) * 2)
|
||||
#define UNPROTECT_HDR(p) VALGRIND_MAKE_MEM_DEFINED(p, sizeof(Header))
|
||||
#define UNPROTECT_FREE_HDR(p) VALGRIND_MAKE_MEM_DEFINED(p, sizeof(Header) * 2)
|
||||
|
||||
|
||||
#if USE_VALGRIND
|
||||
static void _unprotect_pool(void * pool)
|
||||
{
|
||||
Header * cur = (Header*)pool;
|
||||
for (;;)
|
||||
{
|
||||
UNPROTECT_HDR(cur);
|
||||
if (isFree(cur))
|
||||
UNPROTECT_HDR(cur + 1);
|
||||
|
||||
if (isNil(cur->next))
|
||||
break;
|
||||
|
||||
cur = HDR_PTR(cur->next);
|
||||
}
|
||||
}
|
||||
|
||||
static void _protect_pool(void * pool)
|
||||
{
|
||||
Header * cur = (Header*)pool;
|
||||
while (cur)
|
||||
{
|
||||
Header * next = isNil(cur->next) ? NULL : HDR_PTR(cur->next);
|
||||
|
||||
if (isFree(cur))
|
||||
VALGRIND_MAKE_MEM_NOACCESS(cur, (char*)next - (char*)cur);
|
||||
else
|
||||
PROTECT_HDR(cur);
|
||||
|
||||
cur = next;
|
||||
}
|
||||
}
|
||||
#define assert_is_pool(pool) assert(VALGRIND_MEMPOOL_EXISTS(pool));
|
||||
|
||||
#else
|
||||
|
||||
static void _unprotect_pool(void * pool){(void)pool;}
|
||||
static void _protect_pool(void * pool){(void)pool;}
|
||||
#define assert_is_pool(pool) ((void)0)
|
||||
#endif
|
||||
|
||||
// internal version that does not unprotect/protect the pool
|
||||
static int _yalloc_defrag_in_progress(void * pool)
|
||||
{
|
||||
// fragmentation is indicated by a free list with one entry: the last block of the pool, which has its "free"-bit cleared.
|
||||
Header * p = (Header*)pool;
|
||||
if (isNil(p->prev))
|
||||
return 0;
|
||||
|
||||
return !(HDR_PTR(p->prev)->prev & 1);
|
||||
}
|
||||
|
||||
int yalloc_defrag_in_progress(void * pool)
|
||||
{
|
||||
_unprotect_pool(pool);
|
||||
int ret = _yalloc_defrag_in_progress(pool);
|
||||
_protect_pool(pool);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if YALLOC_INTERNAL_VALIDATE
|
||||
|
||||
static size_t _count_free_list_occurences(Header * pool, Header * blk)
|
||||
{
|
||||
int n = 0;
|
||||
if (!isNil(pool->prev))
|
||||
{
|
||||
Header * cur = HDR_PTR(pool->prev);
|
||||
for (;;)
|
||||
{
|
||||
if (cur == blk)
|
||||
++n;
|
||||
|
||||
if (isNil(cur[1].next))
|
||||
break;
|
||||
|
||||
cur = HDR_PTR(cur[1].next);
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static size_t _count_addr_list_occurences(Header * pool, Header * blk)
|
||||
{
|
||||
size_t n = 0;
|
||||
Header * cur = pool;
|
||||
for (;;)
|
||||
{
|
||||
if (cur == blk)
|
||||
++n;
|
||||
|
||||
if (isNil(cur->next))
|
||||
break;
|
||||
|
||||
cur = HDR_PTR(cur->next);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static void _validate_user_ptr(void * pool, void * p)
|
||||
{
|
||||
Header * hdr = (Header*)p - 1;
|
||||
size_t n = _count_addr_list_occurences((Header*)pool, hdr);
|
||||
assert(n == 1 && !isFree(hdr));
|
||||
}
|
||||
|
||||
/**
|
||||
Validates if all the invariants of a pool are intact.
|
||||
|
||||
This is very expensive when there are enough blocks in the heap (quadratic complexity!).
|
||||
*/
|
||||
static void _yalloc_validate(void * pool_)
|
||||
{
|
||||
Header * pool = (Header*)pool_;
|
||||
Header * cur = pool;
|
||||
|
||||
assert(!isNil(pool->next)); // there must always be at least two blocks: a free/used one and the final block at the end
|
||||
|
||||
if (_yalloc_defrag_in_progress(pool))
|
||||
{
|
||||
Header * prevUsed = NULL;
|
||||
while (!isNil(cur->next))
|
||||
{
|
||||
if (!isFree(cur))
|
||||
{ // it is a used block
|
||||
Header * newAddr = cur == pool ? pool : HDR_PTR(cur->prev);
|
||||
assert(newAddr <= cur);
|
||||
assert(newAddr >= pool);
|
||||
|
||||
if (prevUsed)
|
||||
{
|
||||
Header * prevNewAddr = prevUsed == pool ? pool : HDR_PTR(prevUsed->prev);
|
||||
size_t prevBruttoSize = (char*)HDR_PTR(prevUsed->next) - (char*)prevUsed;
|
||||
if (isPadded(prevUsed))
|
||||
prevBruttoSize -= 4; // remove padding
|
||||
assert((char*)newAddr == (char*)prevNewAddr + prevBruttoSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(newAddr == pool);
|
||||
}
|
||||
|
||||
prevUsed = cur;
|
||||
}
|
||||
|
||||
cur = HDR_PTR(cur->next);
|
||||
}
|
||||
|
||||
assert(cur == HDR_PTR(pool->prev)); // the free-list should point to the last block
|
||||
assert(!isFree(cur)); // the last block must not be free
|
||||
}
|
||||
else
|
||||
{
|
||||
Header * prev = NULL;
|
||||
|
||||
// iterate blocks in address order
|
||||
for (;;)
|
||||
{
|
||||
if (prev)
|
||||
{
|
||||
Header * x = HDR_PTR(cur->prev);
|
||||
assert(x == prev);
|
||||
}
|
||||
|
||||
int n = _count_free_list_occurences(pool, cur);
|
||||
if (isFree(cur))
|
||||
{ // it is a free block
|
||||
assert(n == 1);
|
||||
assert(!isPadded(cur)); // free blocks must have a zero padding-bit
|
||||
|
||||
if (prev)
|
||||
{
|
||||
assert(!isFree(prev)); // free blocks must not be direct neighbours
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(n == 0);
|
||||
}
|
||||
|
||||
if (isNil(cur->next))
|
||||
break;
|
||||
|
||||
Header * next = HDR_PTR(cur->next);
|
||||
assert((char*)next >= (char*)cur + sizeof(Header) * 2);
|
||||
prev = cur;
|
||||
cur = next;
|
||||
}
|
||||
|
||||
assert(isNil(cur->next));
|
||||
|
||||
if (!isNil(pool->prev))
|
||||
{
|
||||
// iterate free-list
|
||||
Header * f = HDR_PTR(pool->prev);
|
||||
assert(isNil(f[1].prev));
|
||||
for (;;)
|
||||
{
|
||||
assert(isFree(f)); // must be free
|
||||
|
||||
int n = _count_addr_list_occurences(pool, f);
|
||||
assert(n == 1);
|
||||
|
||||
if (isNil(f[1].next))
|
||||
break;
|
||||
|
||||
f = HDR_PTR(f[1].next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
static void _yalloc_validate(void * pool){(void)pool;}
|
||||
static void _validate_user_ptr(void * pool, void * p){(void)pool; (void)p;}
|
||||
#endif
|
||||
|
||||
int yalloc_init(void * pool, size_t size)
|
||||
{
|
||||
if (size > MAX_POOL_SIZE)
|
||||
return -1;
|
||||
|
||||
// TODO: Error when pool is not properly aligned
|
||||
|
||||
// TODO: Error when size is not a multiple of the alignment?
|
||||
while (size % sizeof(Header))
|
||||
--size;
|
||||
|
||||
if(size < sizeof(Header) * 3)
|
||||
return -1;
|
||||
|
||||
VALGRIND_CREATE_MEMPOOL(pool, 0, 0);
|
||||
|
||||
Header * first = (Header*)pool;
|
||||
Header * last = (Header*)((char*)pool + size) - 1;
|
||||
|
||||
MARK_NEW_FREE_HDR(first);
|
||||
MARK_NEW_HDR(first);
|
||||
|
||||
first->prev = HDR_OFFSET(first) | 1;
|
||||
first->next = HDR_OFFSET(last);
|
||||
first[1].prev = NIL;
|
||||
first[1].next = NIL;
|
||||
|
||||
last->prev = HDR_OFFSET(first);
|
||||
last->next = NIL;
|
||||
|
||||
_unprotect_pool(pool);
|
||||
_yalloc_validate(pool);
|
||||
_protect_pool(pool);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void yalloc_deinit(void * pool)
|
||||
{
|
||||
#if USE_VALGRIND
|
||||
VALGRIND_DESTROY_MEMPOOL(pool);
|
||||
|
||||
Header * last = (Header*)pool;
|
||||
UNPROTECT_HDR(last);
|
||||
while (!isNil(last->next))
|
||||
{
|
||||
Header * next = HDR_PTR(last->next);
|
||||
UNPROTECT_HDR(next);
|
||||
last = next;
|
||||
}
|
||||
|
||||
VALGRIND_MAKE_MEM_UNDEFINED(pool, (char*)(last + 1) - (char*)pool);
|
||||
#else
|
||||
(void)pool;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void * yalloc_alloc(void * pool, size_t size)
|
||||
{
|
||||
assert_is_pool(pool);
|
||||
_unprotect_pool(pool);
|
||||
assert(!_yalloc_defrag_in_progress(pool));
|
||||
_yalloc_validate(pool);
|
||||
if (!size)
|
||||
{
|
||||
_protect_pool(pool);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Header * root = (Header*)pool;
|
||||
if (isNil(root->prev))
|
||||
{
|
||||
_protect_pool(pool);
|
||||
return NULL; /* no free block, no chance to allocate anything */ // TODO: Just read up which C standard supports single line comments and then fucking use them!
|
||||
}
|
||||
|
||||
/* round up to alignment */
|
||||
size = ALIGN(size, 32);
|
||||
|
||||
size_t bruttoSize = size + sizeof(Header);
|
||||
Header * prev = NULL;
|
||||
Header * cur = HDR_PTR(root->prev);
|
||||
for (;;)
|
||||
{
|
||||
size_t curSize = (char*)HDR_PTR(cur->next) - (char*)cur; /* size of the block, including its header */
|
||||
|
||||
if (curSize >= bruttoSize) // it is big enough
|
||||
{
|
||||
// take action for unused space in the free block
|
||||
if (curSize >= bruttoSize + sizeof(Header) * 2)
|
||||
{ // the leftover space is big enough to make it a free block
|
||||
// Build a free block from the unused space and insert it into the list of free blocks after the current free block
|
||||
Header * tail = (Header*)((char*)cur + bruttoSize);
|
||||
MARK_NEW_FREE_HDR(tail);
|
||||
|
||||
// update address-order-list
|
||||
tail->next = cur->next;
|
||||
tail->prev = HDR_OFFSET(cur) | 1;
|
||||
HDR_PTR(cur->next)->prev = HDR_OFFSET(tail); // NOTE: We know the next block is used because free blocks are never neighbours. So we don't have to care about the lower bit which would be set for the prev of a free block.
|
||||
cur->next = HDR_OFFSET(tail);
|
||||
|
||||
// update list of free blocks
|
||||
tail[1].next = cur[1].next;
|
||||
// NOTE: tail[1].prev is updated in the common path below (assignment to "HDR_PTR(cur[1].next)[1].prev")
|
||||
|
||||
if (!isNil(cur[1].next))
|
||||
HDR_PTR(cur[1].next)[1].prev = HDR_OFFSET(tail);
|
||||
cur[1].next = HDR_OFFSET(tail);
|
||||
}
|
||||
else if (curSize > bruttoSize)
|
||||
{ // there will be unused space, but not enough to insert a free header
|
||||
internal_assert(curSize - bruttoSize == sizeof(Header)); // unused space must be enough to build a free-block or it should be exactly the size of a Header
|
||||
cur->next |= 1; // set marker for "has unused trailing space"
|
||||
}
|
||||
else
|
||||
{
|
||||
internal_assert(curSize == bruttoSize);
|
||||
}
|
||||
|
||||
cur->prev &= NIL; // clear marker for "is a free block"
|
||||
|
||||
// remove from linked list of free blocks
|
||||
if (prev)
|
||||
prev[1].next = cur[1].next;
|
||||
else
|
||||
{
|
||||
uint32_t freeBit = isFree(root);
|
||||
root->prev = (cur[1].next & NIL) | freeBit;
|
||||
}
|
||||
|
||||
if (!isNil(cur[1].next))
|
||||
HDR_PTR(cur[1].next)[1].prev = prev ? HDR_OFFSET(prev) : NIL;
|
||||
|
||||
_yalloc_validate(pool);
|
||||
VALGRIND_MEMPOOL_ALLOC(pool, cur + 1, size);
|
||||
_protect_pool(pool);
|
||||
return cur + 1; // return address after the header
|
||||
}
|
||||
|
||||
if (isNil(cur[1].next))
|
||||
break;
|
||||
|
||||
prev = cur;
|
||||
cur = HDR_PTR(cur[1].next);
|
||||
}
|
||||
|
||||
_yalloc_validate(pool);
|
||||
_protect_pool(pool);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Removes a block from the free-list and moves the pools first-free-bock pointer to its successor if it pointed to that block.
|
||||
static void unlink_from_free_list(Header * pool, Header * blk)
|
||||
{
|
||||
// update the pools pointer to the first block in the free list if necessary
|
||||
if (isNil(blk[1].prev))
|
||||
{ // the block is the first in the free-list
|
||||
// make the pools first-free-pointer point to the next in the free list
|
||||
uint32_t freeBit = isFree(pool);
|
||||
pool->prev = (blk[1].next & NIL) | freeBit;
|
||||
}
|
||||
else
|
||||
HDR_PTR(blk[1].prev)[1].next = blk[1].next;
|
||||
|
||||
if (!isNil(blk[1].next))
|
||||
HDR_PTR(blk[1].next)[1].prev = blk[1].prev;
|
||||
}
|
||||
|
||||
size_t yalloc_block_size(void * pool, void * p)
|
||||
{
|
||||
Header * a = (Header*)p - 1;
|
||||
UNPROTECT_HDR(a);
|
||||
Header * b = HDR_PTR(a->next);
|
||||
size_t payloadSize = (char*)b - (char*)p;
|
||||
if (isPadded(a))
|
||||
payloadSize -= sizeof(Header);
|
||||
PROTECT_HDR(a);
|
||||
return payloadSize;
|
||||
}
|
||||
|
||||
void yalloc_free(void * pool_, void * p)
|
||||
{
|
||||
assert_is_pool(pool_);
|
||||
assert(!yalloc_defrag_in_progress(pool_));
|
||||
if (!p)
|
||||
return;
|
||||
|
||||
_unprotect_pool(pool_);
|
||||
|
||||
Header * pool = (Header*)pool_;
|
||||
Header * cur = (Header*)p - 1;
|
||||
|
||||
// get pointers to previous/next block in address order
|
||||
Header * prev = cur == pool || isNil(cur->prev) ? NULL : HDR_PTR(cur->prev);
|
||||
Header * next = isNil(cur->next) ? NULL : HDR_PTR(cur->next);
|
||||
|
||||
int prevFree = prev && isFree(prev);
|
||||
int nextFree = next && isFree(next);
|
||||
|
||||
#if USE_VALGRIND
|
||||
{
|
||||
unsigned errs = VALGRIND_COUNT_ERRORS;
|
||||
VALGRIND_MEMPOOL_FREE(pool, p);
|
||||
if (VALGRIND_COUNT_ERRORS > errs)
|
||||
{ // early exit if the free was invalid (so we get a valgrind error and don't mess up the pool, which is helpful for testing if invalid frees are detected by valgrind)
|
||||
_protect_pool(pool_);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
_validate_user_ptr(pool_, p);
|
||||
|
||||
if (prevFree && nextFree)
|
||||
{ // the freed block has two free neighbors
|
||||
unlink_from_free_list(pool, prev);
|
||||
unlink_from_free_list(pool, next);
|
||||
|
||||
// join prev, cur and next
|
||||
prev->next = next->next;
|
||||
HDR_PTR(next->next)->prev = cur->prev;
|
||||
|
||||
// prev is now the block we want to push onto the free-list
|
||||
cur = prev;
|
||||
}
|
||||
else if (prevFree)
|
||||
{
|
||||
unlink_from_free_list(pool, prev);
|
||||
|
||||
// join prev and cur
|
||||
prev->next = cur->next;
|
||||
HDR_PTR(cur->next)->prev = cur->prev;
|
||||
|
||||
// prev is now the block we want to push onto the free-list
|
||||
cur = prev;
|
||||
}
|
||||
else if (nextFree)
|
||||
{
|
||||
unlink_from_free_list(pool, next);
|
||||
|
||||
// join cur and next
|
||||
cur->next = next->next;
|
||||
HDR_PTR(next->next)->prev = next->prev & NIL;
|
||||
}
|
||||
|
||||
// if there is a previous block and that block has padding then we want to grow the new free block into that padding
|
||||
if (cur != pool && !isNil(cur->prev))
|
||||
{ // there is a previous block
|
||||
Header * left = HDR_PTR(cur->prev);
|
||||
if (isPadded(left))
|
||||
{ // the previous block has padding, so extend the current block to consume move the padding to the current free block
|
||||
Header * grown = cur - 1;
|
||||
MARK_NEW_HDR(grown);
|
||||
grown->next = cur->next;
|
||||
grown->prev = cur->prev;
|
||||
left->next = HDR_OFFSET(grown);
|
||||
if (!isNil(cur->next))
|
||||
HDR_PTR(cur->next)->prev = HDR_OFFSET(grown);
|
||||
|
||||
cur = grown;
|
||||
}
|
||||
}
|
||||
|
||||
cur->prev |= 1; // it becomes a free block
|
||||
cur->next &= NIL; // reset padding-bit
|
||||
UNPROTECT_HDR(cur + 1);
|
||||
cur[1].prev = NIL; // it will be the first free block in the free list, so it has no prevFree
|
||||
|
||||
if (!isNil(pool->prev))
|
||||
{ // the free-list was already non-empty
|
||||
HDR_PTR(pool->prev)[1].prev = HDR_OFFSET(cur); // make the first entry in the free list point back to the new free block (it will become the first one)
|
||||
cur[1].next = pool->prev; // the next free block is the first of the old free-list
|
||||
}
|
||||
else
|
||||
cur[1].next = NIL; // free-list was empty, so there is no successor
|
||||
|
||||
VALGRIND_MAKE_MEM_NOACCESS(cur + 2, (char*)HDR_PTR(cur->next) - (char*)(cur + 2));
|
||||
|
||||
// now the freed block is the first in the free-list
|
||||
|
||||
// update the offset to the first element of the free list
|
||||
uint32_t freeBit = isFree(pool); // remember the free-bit of the offset
|
||||
pool->prev = HDR_OFFSET(cur) | freeBit; // update the offset and restore the free-bit
|
||||
_yalloc_validate(pool);
|
||||
_protect_pool(pool);
|
||||
}
|
||||
|
||||
size_t yalloc_count_free(void * pool_)
|
||||
{
|
||||
assert_is_pool(pool_);
|
||||
_unprotect_pool(pool_);
|
||||
assert(!_yalloc_defrag_in_progress(pool_));
|
||||
Header * pool = (Header*)pool_;
|
||||
size_t bruttoFree = 0;
|
||||
Header * cur = pool;
|
||||
|
||||
_yalloc_validate(pool);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (isFree(cur))
|
||||
{ // it is a free block
|
||||
bruttoFree += (char*)HDR_PTR(cur->next) - (char*)cur;
|
||||
}
|
||||
else
|
||||
{ // it is a used block
|
||||
if (isPadded(cur))
|
||||
{ // the used block is padded
|
||||
bruttoFree += sizeof(Header);
|
||||
}
|
||||
}
|
||||
|
||||
if (isNil(cur->next))
|
||||
break;
|
||||
|
||||
cur = HDR_PTR(cur->next);
|
||||
}
|
||||
|
||||
_protect_pool(pool);
|
||||
|
||||
if (bruttoFree < sizeof(Header))
|
||||
{
|
||||
internal_assert(!bruttoFree); // free space should always be a multiple of sizeof(Header)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return bruttoFree - sizeof(Header);
|
||||
}
|
||||
|
||||
size_t yalloc_count_continuous(void * pool_)
|
||||
{
|
||||
assert_is_pool(pool_);
|
||||
_unprotect_pool(pool_);
|
||||
assert(!_yalloc_defrag_in_progress(pool_));
|
||||
Header * pool = (Header*)pool_;
|
||||
size_t largestFree = 0;
|
||||
Header * cur = pool;
|
||||
|
||||
_yalloc_validate(pool);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (isFree(cur))
|
||||
{ // it is a free block
|
||||
size_t temp = (uintptr_t)HDR_PTR(cur->next) - (uintptr_t)cur;
|
||||
if(temp > largestFree)
|
||||
largestFree = temp;
|
||||
}
|
||||
|
||||
if (isNil(cur->next))
|
||||
break;
|
||||
|
||||
cur = HDR_PTR(cur->next);
|
||||
}
|
||||
|
||||
_protect_pool(pool);
|
||||
|
||||
if (largestFree < sizeof(Header))
|
||||
{
|
||||
internal_assert(!largestFree); // free space should always be a multiple of sizeof(Header)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return largestFree - sizeof(Header);
|
||||
}
|
||||
|
||||
void * yalloc_first_used(void * pool)
|
||||
{
|
||||
assert_is_pool(pool);
|
||||
_unprotect_pool(pool);
|
||||
Header * blk = (Header*)pool;
|
||||
while (!isNil(blk->next))
|
||||
{
|
||||
if (!isFree(blk))
|
||||
{
|
||||
_protect_pool(pool);
|
||||
return blk + 1;
|
||||
}
|
||||
|
||||
blk = HDR_PTR(blk->next);
|
||||
}
|
||||
|
||||
_protect_pool(pool);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void * yalloc_next_used(void * pool, void * p)
|
||||
{
|
||||
assert_is_pool(pool);
|
||||
_unprotect_pool(pool);
|
||||
_validate_user_ptr(pool, p);
|
||||
Header * prev = (Header*)p - 1;
|
||||
assert(!isNil(prev->next)); // the last block should never end up as input to this function (because it is not user-visible)
|
||||
|
||||
Header * blk = HDR_PTR(prev->next);
|
||||
while (!isNil(blk->next))
|
||||
{
|
||||
if (!isFree(blk))
|
||||
{
|
||||
_protect_pool(pool);
|
||||
return blk + 1;
|
||||
}
|
||||
|
||||
blk = HDR_PTR(blk->next);
|
||||
}
|
||||
|
||||
_protect_pool(pool);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void yalloc_defrag_start(void * pool_)
|
||||
{
|
||||
assert_is_pool(pool_);
|
||||
_unprotect_pool(pool_);
|
||||
assert(!_yalloc_defrag_in_progress(pool_));
|
||||
Header * pool = (Header*)pool_;
|
||||
|
||||
// iterate over all blocks in address order and store the post-defragment address of used blocks in their "prev" field
|
||||
size_t end = 0; // offset for the next used block
|
||||
Header * blk = (Header*)pool;
|
||||
for (; !isNil(blk->next); blk = HDR_PTR(blk->next))
|
||||
{
|
||||
if (!isFree(blk))
|
||||
{ // it is a used block
|
||||
blk->prev = end >> 1;
|
||||
internal_assert((char*)HDR_PTR(blk->prev) == (char*)pool + end);
|
||||
|
||||
size_t bruttoSize = (char*)HDR_PTR(blk->next) - (char*)blk;
|
||||
|
||||
if (isPadded(blk))
|
||||
{ // the block is padded
|
||||
bruttoSize -= sizeof(Header);
|
||||
}
|
||||
|
||||
end += bruttoSize;
|
||||
internal_assert(end % sizeof(Header) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
// blk is now the last block (the dummy "used" block at the end of the pool)
|
||||
internal_assert(isNil(blk->next));
|
||||
internal_assert(!isFree(blk));
|
||||
|
||||
// mark the pool as "defragementation in progress"
|
||||
uint32_t freeBit = isFree(pool);
|
||||
pool->prev = (HDR_OFFSET(blk) & NIL) | freeBit;
|
||||
|
||||
_yalloc_validate(pool);
|
||||
internal_assert(yalloc_defrag_in_progress(pool));
|
||||
_protect_pool(pool);
|
||||
}
|
||||
|
||||
void * yalloc_defrag_address(void * pool_, void * p)
|
||||
{
|
||||
assert_is_pool(pool_);
|
||||
assert(yalloc_defrag_in_progress(pool_));
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
Header * pool = (Header*)pool_;
|
||||
|
||||
_unprotect_pool(pool);
|
||||
_validate_user_ptr(pool_, p);
|
||||
|
||||
if (pool + 1 == p)
|
||||
return pool + 1; // "prev" of the first block points to the last used block to mark the pool as "defragmentation in progress"
|
||||
|
||||
Header * blk = (Header*)p - 1;
|
||||
|
||||
void * defragP = HDR_PTR(blk->prev) + 1;
|
||||
|
||||
_protect_pool(pool);
|
||||
return defragP;
|
||||
}
|
||||
|
||||
void yalloc_defrag_commit(void * pool_)
|
||||
{
|
||||
assert_is_pool(pool_);
|
||||
_unprotect_pool(pool_);
|
||||
assert(_yalloc_defrag_in_progress(pool_));
|
||||
Header * pool = (Header*)pool_;
|
||||
|
||||
// iterate over all blocks in address order and move them
|
||||
size_t end = 0; // offset for the next used block
|
||||
Header * blk = pool;
|
||||
Header * lastUsed = NULL;
|
||||
while (!isNil(blk->next))
|
||||
{
|
||||
if (!isFree(blk))
|
||||
{ // it is a used block
|
||||
size_t bruttoSize = (char*)HDR_PTR(blk->next) - (char*)blk;
|
||||
|
||||
if (isPadded(blk))
|
||||
{ // the block is padded
|
||||
bruttoSize -= sizeof(Header);
|
||||
}
|
||||
|
||||
Header * next = HDR_PTR(blk->next);
|
||||
|
||||
blk->prev = lastUsed ? HDR_OFFSET(lastUsed) : NIL;
|
||||
blk->next = (end + bruttoSize) >> 1;
|
||||
|
||||
lastUsed = (Header*)((char*)pool + end);
|
||||
VALGRIND_MAKE_MEM_UNDEFINED(lastUsed, (char*)blk - (char*)lastUsed);
|
||||
memmove(lastUsed, blk, bruttoSize);
|
||||
VALGRIND_MEMPOOL_CHANGE(pool, blk + 1, lastUsed + 1, bruttoSize - sizeof(Header));
|
||||
|
||||
end += bruttoSize;
|
||||
blk = next;
|
||||
}
|
||||
else
|
||||
blk = HDR_PTR(blk->next);
|
||||
}
|
||||
|
||||
// blk is now the last block (the dummy "used" block at the end of the pool)
|
||||
internal_assert(isNil(blk->next));
|
||||
internal_assert(!isFree(blk));
|
||||
|
||||
if (lastUsed)
|
||||
{
|
||||
Header * gap = HDR_PTR(lastUsed->next);
|
||||
if (gap == blk)
|
||||
{ // there is no gap
|
||||
pool->prev = NIL; // the free list is empty
|
||||
blk->prev = HDR_OFFSET(lastUsed);
|
||||
}
|
||||
else if (blk - gap > 1)
|
||||
{ // the gap is big enouogh for a free Header
|
||||
|
||||
// set a free list that contains the gap as only element
|
||||
gap->prev = HDR_OFFSET(lastUsed) | 1;
|
||||
gap->next = HDR_OFFSET(blk);
|
||||
gap[1].prev = NIL;
|
||||
gap[1].next = NIL;
|
||||
pool->prev = blk->prev = HDR_OFFSET(gap);
|
||||
}
|
||||
else
|
||||
{ // there is a gap, but it is too small to be used as free-list-node, so just make it padding of the last used block
|
||||
lastUsed->next = HDR_OFFSET(blk) | 1;
|
||||
pool->prev = NIL;
|
||||
blk->prev = HDR_OFFSET(lastUsed);
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // the pool is empty
|
||||
pool->prev = 1;
|
||||
}
|
||||
|
||||
internal_assert(!_yalloc_defrag_in_progress(pool));
|
||||
_yalloc_validate(pool);
|
||||
_protect_pool(pool);
|
||||
}
|
176
third_party/gldc/src/yalloc/yalloc.h
vendored
Normal file
176
third_party/gldc/src/yalloc/yalloc.h
vendored
Normal file
@ -0,0 +1,176 @@
|
||||
/**
|
||||
@file
|
||||
|
||||
API of the yalloc allocator.
|
||||
*/
|
||||
|
||||
#ifndef YALLOC_H
|
||||
#define YALLOC_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
Maximum supported pool size. yalloc_init() will fail for larger pools.
|
||||
*/
|
||||
#define MAX_POOL_SIZE ((2 << 24) - 4)
|
||||
|
||||
/**
|
||||
Creates a pool inside a given buffer.
|
||||
|
||||
Pools must be deinitialized with yalloc_deinit() when they are no longer needed.
|
||||
|
||||
@param pool The starting address of the pool. It must have at least 16bit
|
||||
alignment (internal structure uses 16bit integers). Allocations are placed at
|
||||
32bit boundaries starting from this address, so if the user data should be
|
||||
32bit aligned then this address has to be 32bit aligned. Typically an address
|
||||
of static memory, or an array on the stack is used if the pool is only used
|
||||
temporarily.
|
||||
@param size Size of the pool.
|
||||
@return 0 on success, nonzero if the size is not supported.
|
||||
*/
|
||||
int yalloc_init(void * pool, size_t size);
|
||||
|
||||
/**
|
||||
Deinitializes the buffer that is used by the pool and makes it available for other use.
|
||||
|
||||
The content of the buffer is undefined after this.
|
||||
|
||||
@param pool The starting address of an initialized pool.
|
||||
*/
|
||||
void yalloc_deinit(void * pool);
|
||||
|
||||
/**
|
||||
Allocates a block of memory from a pool.
|
||||
|
||||
This function mimics malloc().
|
||||
|
||||
The pool must not be in the "defragmenting" state when this function is called.
|
||||
|
||||
@param pool The starting address of an initialized pool.
|
||||
@param size Number of bytes to allocate.
|
||||
@return Allocated buffer or \c NULL if there was no free range that could serve
|
||||
the allocation. See @ref yalloc_defrag_start() for a way to remove
|
||||
fragmentation which may cause allocations to fail even when there is enough
|
||||
space in total.
|
||||
*/
|
||||
void * yalloc_alloc(void * pool, size_t size);
|
||||
|
||||
/**
|
||||
Returns an allocation to a pool.
|
||||
|
||||
This function mimics free().
|
||||
|
||||
The pool must not be in the "defragmenting" state when this function is called.
|
||||
|
||||
@param pool The starting address of the initialized pool the allocation comes from.
|
||||
@param p An address that was returned from yalloc_alloc() of the same pool.
|
||||
*/
|
||||
void yalloc_free(void * pool, void * p);
|
||||
|
||||
/**
|
||||
Returns the maximum size of a successful allocation (assuming a completely unfragmented heap).
|
||||
|
||||
After defragmentation the first allocation with the returned size is guaranteed to succeed.
|
||||
|
||||
@param pool The starting address of an initialized pool.
|
||||
@return Number of bytes that can be allocated (assuming the pool is defragmented).
|
||||
*/
|
||||
size_t yalloc_count_free(void * pool);
|
||||
|
||||
/**
|
||||
Returns the maximum continuous free area.
|
||||
|
||||
@param pool The starting address of an initialized pool.
|
||||
@return Number of free bytes that exist continuously.
|
||||
*/
|
||||
size_t yalloc_count_continuous(void * pool_);
|
||||
|
||||
/**
|
||||
Queries the usable size of an allocated block.
|
||||
|
||||
@param pool The starting address of the initialized pool the allocation comes from.
|
||||
@param p An address that was returned from yalloc_alloc() of the same pool.
|
||||
@return Size of the memory block. This is the size passed to @ref yalloc_alloc() rounded up to 4.
|
||||
*/
|
||||
size_t yalloc_block_size(void * pool, void * p);
|
||||
|
||||
/**
|
||||
Finds the first (in address order) allocation of a pool.
|
||||
|
||||
@param pool The starting address of an initialized pool.
|
||||
@return Address of the allocation the lowest address inside the pool (this is
|
||||
what @ref yalloc_alloc() returned), or \c NULL if there is no used block.
|
||||
*/
|
||||
void * yalloc_first_used(void * pool);
|
||||
|
||||
/**
|
||||
Given a pointer to an allocation finds the next (in address order) used block of a pool.
|
||||
|
||||
@param pool The starting address of the initialized pool the allocation comes from.
|
||||
@param p Pointer to an allocation in that pool, typically comes from a previous
|
||||
call to @ref yalloc_first_used()
|
||||
*/
|
||||
void * yalloc_next_used(void * pool, void * p);
|
||||
|
||||
/**
|
||||
Starts defragmentation for a pool.
|
||||
|
||||
Allocations will stay where they are. But the pool is put in the "defagmenting"
|
||||
state (see @ref yalloc_defrag_in_progress()).
|
||||
|
||||
The pool must not be in the "defragmenting" state when this function is called.
|
||||
The pool is put into the "defragmenting" state by this function.
|
||||
|
||||
@param pool The starting address of an initialized pool.
|
||||
*/
|
||||
void yalloc_defrag_start(void * pool);
|
||||
|
||||
/**
|
||||
Returns the address that an allocation will have after @ref yalloc_defrag_commit() is called.
|
||||
|
||||
The pool must be in the "defragmenting" state when this function is called.
|
||||
|
||||
@param pool The starting address of the initialized pool the allocation comes from.
|
||||
@param p Pointer to an allocation in that pool.
|
||||
@return The address the alloation will have after @ref yalloc_defrag_commit() is called.
|
||||
*/
|
||||
void * yalloc_defrag_address(void * pool, void * p);
|
||||
|
||||
/**
|
||||
Finishes the defragmentation.
|
||||
|
||||
The content of all allocations in the pool will be moved to the address that
|
||||
was reported by @ref yalloc_defrag_address(). The pool will then have only one
|
||||
free block. This means that an <tt>yalloc_alloc(pool, yalloc_count_free(pool))</tt>
|
||||
will succeed.
|
||||
|
||||
The pool must be in the "defragmenting" state when this function is called. The
|
||||
pool is put back to normal state by this function.
|
||||
|
||||
@param pool The starting address of an initialized pool.
|
||||
*/
|
||||
void yalloc_defrag_commit(void * pool);
|
||||
|
||||
/**
|
||||
Tells if the pool is in the "defragmenting" state (after a @ref yalloc_defrag_start() and before a @ref yalloc_defrag_commit()).
|
||||
|
||||
@param pool The starting address of an initialized pool.
|
||||
@return Nonzero if the pool is currently in the "defragmenting" state.
|
||||
*/
|
||||
int yalloc_defrag_in_progress(void * pool);
|
||||
|
||||
|
||||
/**
|
||||
Helper function that dumps the state of the pool to stdout.
|
||||
|
||||
This function is only available if build with <tt>yalloc_dump.c</tt>. This
|
||||
function only exists for debugging purposes and can be ignored by normal users
|
||||
that are not interested in the internal structure of the implementation.
|
||||
|
||||
@param pool The starting address of an initialized pool.
|
||||
@param name A string that is used as "Title" for the output.
|
||||
*/
|
||||
void yalloc_dump(void * pool, char * name);
|
||||
|
||||
|
||||
#endif // YALLOC_H
|
39
third_party/gldc/src/yalloc/yalloc_dump.c
vendored
Normal file
39
third_party/gldc/src/yalloc/yalloc_dump.c
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
#include "yalloc_internals.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static void printOffset(void * pool, char * name, uint16_t offset)
|
||||
{
|
||||
if (isNil(offset))
|
||||
printf(" %s: nil\n", name);
|
||||
else
|
||||
printf(" %s: %td\n", name, (char*)HDR_PTR(offset) - (char*)pool);
|
||||
}
|
||||
|
||||
void yalloc_dump(void * pool, char * name)
|
||||
{
|
||||
printf("---- %s ----\n", name);
|
||||
Header * cur = (Header*)pool;
|
||||
for (;;)
|
||||
{
|
||||
printf(isFree(cur) ? "%td: free @%p\n" : "%td: used @%p\n", (char*)cur - (char*)pool, cur);
|
||||
printOffset(pool, cur == pool ? "first free" : "prev", cur->prev);
|
||||
printOffset(pool, "next", cur->next);
|
||||
if (isFree(cur))
|
||||
{
|
||||
printOffset(pool, "prevFree", cur[1].prev);
|
||||
printOffset(pool, "nextFree", cur[1].next);
|
||||
}
|
||||
else
|
||||
printf(" payload includes padding: %i\n", isPadded(cur));
|
||||
|
||||
if (isNil(cur->next))
|
||||
break;
|
||||
|
||||
printf(" %td bytes payload\n", (char*)HDR_PTR(cur->next) - (char*)cur - sizeof(Header));
|
||||
|
||||
cur = HDR_PTR(cur->next);
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
}
|
63
third_party/gldc/src/yalloc/yalloc_internals.h
vendored
Normal file
63
third_party/gldc/src/yalloc/yalloc_internals.h
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
#ifndef YALLOC_INTERNALS_H
|
||||
#define YALLOC_INTERNALS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t prev; // low bit set if free
|
||||
uint32_t next; // for used blocks: low bit set if unused header at the end
|
||||
|
||||
/* We need user data to be 32-byte aligned, so the header needs
|
||||
* to be 32 bytes in size (as user data follows the header) */
|
||||
uint8_t padding[32 - (sizeof(uint32_t) * 2)];
|
||||
} Header;
|
||||
|
||||
// NOTE: We have 32bit aligned data and 16bit offsets where the lowest bit is used as flag. So we remove the low bit and shift by 1 to address 128k bytes with the 15bit significant offset bits.
|
||||
|
||||
#define NIL 0xFFFFFFFEu
|
||||
|
||||
// return Header-address for a prev/next
|
||||
#define HDR_PTR(offset) ((Header*)((char*)pool + (((offset) & NIL)<<1)))
|
||||
|
||||
// return a prev/next for a Header-address
|
||||
#define HDR_OFFSET(blockPtr) ((uint32_t)(((char*)blockPtr - (char*)pool) >> 1))
|
||||
|
||||
#ifndef YALLOC_INTERNAL_VALIDATE
|
||||
# ifdef NDEBUG
|
||||
# define YALLOC_INTERNAL_VALIDATE 0
|
||||
# else
|
||||
# define YALLOC_INTERNAL_VALIDATE 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
internal_assert() is used in some places to check internal expections.
|
||||
Activate this if you modify the code to detect problems as early as possible.
|
||||
In other cases this should be deactivated.
|
||||
*/
|
||||
#if 0
|
||||
#define internal_assert assert
|
||||
#else
|
||||
#define internal_assert(condition)((void) 0)
|
||||
#endif
|
||||
|
||||
// detects offsets that point nowhere
|
||||
static inline int isNil(uint32_t offset)
|
||||
{
|
||||
return (offset | 1) == 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
static inline int isFree(Header * hdr)
|
||||
{
|
||||
return hdr->prev & 1;
|
||||
}
|
||||
|
||||
static inline int isPadded(Header * hdr)
|
||||
{
|
||||
return hdr->next & 1;
|
||||
}
|
||||
|
||||
|
||||
#endif // YALLOC_INTERNALS_H
|
Loading…
x
Reference in New Issue
Block a user