diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml index 7d73c103d..e1ff0b0fd 100644 --- a/.github/workflows/build-linux.yml +++ b/.github/workflows/build-linux.yml @@ -1,132 +1,38 @@ -name: Build (Linux, x86_64) +name: Build (Linux, x86-64) on: push: - paths: + paths: &workflow_paths - '**.cpp' - '**.h' - '**.c' - - '**/meson.build' - - 'meson.build' - - '**/CMakeLists.txt' - - 'CMakeLists.txt' + - '**.cc' + - '**.cxx' + - '**.hh' + - '**.hpp' + - '**.hxx' + - '**.inl' + - "**meson.build" + - "flake.nix" - '.github/workflows/build-linux.yml' + pull_request: - paths: - - '**.cpp' - - '**.h' - - '**.c' - - '**/meson.build' - - 'meson.build' - - '**/CMakeLists.txt' - - 'CMakeLists.txt' - - '.github/workflows/build-linux.yml' + paths: *workflow_paths jobs: build-linux: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - - name: Install system dependencies - run: | - sudo apt-get update - sudo apt-get install -y build-essential ccache python3 python3-pip ninja-build libsdl2-dev libgl-dev libglu1-mesa-dev libpthread-stubs0-dev - python -m pip install meson - # Set a reasonable ccache size - ccache -M 5G || true - - - name: Restore ccache - uses: actions/cache@v4 + - name: Install Nix + uses: cachix/install-nix-action@v31 with: - path: ~/.ccache - key: ${{ runner.os }}-ccache-${{ hashFiles('**/meson.build') }} + nix_path: nixpkgs=channel:nixos-unstable - - name: Restore meson cache - uses: actions/cache@v4 - with: - path: ~/.cache/meson - key: ${{ runner.os }}-meson-${{ hashFiles('**/meson.build') }} + - name: Build + run: nix build --print-build-logs - - name: Configure Meson - env: - CC: "ccache clang" - CXX: "ccache clang++" - CCACHE_DIR: ${{ runner.temp }}/ccache - run: | - mkdir -p "$CCACHE_DIR" - export CCACHE_DIR="$CCACHE_DIR" - meson setup build_release --wipe --buildtype=release --native-file=./scripts/llvm_native.txt - - - name: Build with Meson - env: - CC: "ccache clang" - CXX: "ccache clang++" - CCACHE_DIR: ${{ runner.temp }}/ccache - run: | - export CCACHE_DIR="${{ runner.temp }}/ccache" - # Use all available cores for faster parallel builds - meson compile -C build_release -j $(nproc) -v Minecraft.Client - - - name: Install patchelf - run: sudo apt-get install -y patchelf - - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: minecraft-client-linux-release_exe-${{ github.sha }} - path: build_release/Minecraft.Client/Minecraft.Client - retention-days: 7 - build-linux-debug: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Install system dependencies - run: | - sudo apt-get update - sudo apt-get install -y build-essential ccache python3 python3-pip ninja-build libsdl2-dev libgl-dev libglu1-mesa-dev libpthread-stubs0-dev - python -m pip install meson - # Set a reasonable ccache size - ccache -M 5G || true - - - name: Restore ccache - uses: actions/cache@v4 - with: - path: ~/.ccache - key: ${{ runner.os }}-ccache-debug-${{ hashFiles('**/meson.build') }} - - - name: Restore meson cache - uses: actions/cache@v4 - with: - path: ~/.cache/meson - key: ${{ runner.os }}-meson-debug-${{ hashFiles('**/meson.build') }} - - - name: Configure Meson (debug) - env: - CC: "ccache clang" - CXX: "ccache clang++" - CCACHE_DIR: ${{ runner.temp }}/ccache - run: | - mkdir -p "$CCACHE_DIR" - export CCACHE_DIR="$CCACHE_DIR" - meson setup build_debug --wipe --buildtype=debug --native-file=./scripts/llvm_native.txt - - - name: Build Debug with Meson - env: - CC: "ccache clang" - CXX: "ccache clang++" - CCACHE_DIR: ${{ runner.temp }}/ccache - run: | - export CCACHE_DIR="${{ runner.temp }}/ccache" - # Use all available cores for faster parallel builds - meson compile -C build_debug -j $(nproc) -v Minecraft.Client - - - name: Upload debug executable - uses: actions/upload-artifact@v4 - with: - name: minecraft-client-linux-debug_exe-${{ github.sha }} - path: build_debug/Minecraft.Client/Minecraft.Client - retention-days: 7 + - name: Flake integrity + run: nix flake check diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 4965d4b15..84820ad88 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -1,22 +1,8 @@ -name: Clang Format +name: Format Check on: push: - paths: - - '**.cpp' - - '**.h' - - '**.c' - - '**.cc' - - '**.cxx' - - '**.hh' - - '**.hpp' - - '**.hxx' - - '**.inl' - - '.clang-format' - - '.github/workflows/clang-format.yml' - - '.github/scripts/check-clang-format.sh' - pull_request: - paths: + paths: &workflow_paths - '**.cpp' - '**.h' - '**.c' @@ -30,26 +16,29 @@ on: - '.github/workflows/clang-format.yml' - '.github/scripts/check-clang-format.sh' + pull_request: + paths: *workflow_paths + jobs: clang-format: - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest concurrency: group: clang-format-${{ github.ref }} cancel-in-progress: true steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: - fetch-depth: 0 + fetch-depth: 2 - name: Install clang-format - run: | - sudo apt-get update - sudo apt-get install -y clang-format-19 + uses: daaku/gh-action-apt-install@v4 + with: + packages: clang-format - name: Check changed files env: - CLANG_FORMAT_BIN: clang-format-19 + CLANG_FORMAT_BIN: clang-format EVENT_NAME: ${{ github.event_name }} PR_BASE_REF: ${{ github.event.pull_request.base.ref }} PR_BASE_SHA: ${{ github.event.pull_request.base.sha }} diff --git a/Minecraft.Client/Platform/Common/UI/UIControl.cpp b/Minecraft.Client/Platform/Common/UI/UIControl.cpp index 234d9bb99..9a9426be3 100644 --- a/Minecraft.Client/Platform/Common/UI/UIControl.cpp +++ b/Minecraft.Client/Platform/Common/UI/UIControl.cpp @@ -10,7 +10,12 @@ UIControl::UIControl() { m_controlName = ""; m_isVisible = true; m_bHidden = false; + m_isValid = false; m_eControlType = eNoControl; + m_x = 0; + m_y = 0; + m_width = 0; + m_height = 0; } bool UIControl::setupControl(UIScene* scene, IggyValuePath* parent, @@ -20,6 +25,7 @@ bool UIControl::setupControl(UIScene* scene, IggyValuePath* parent, rrbool res = IggyValuePathMakeNameRef(&m_iggyPath, parent, controlName.c_str()); + m_isValid = res ? true : false; m_nameXPos = registerFastName(L"x"); m_nameYPos = registerFastName(L"y"); @@ -28,16 +34,32 @@ bool UIControl::setupControl(UIScene* scene, IggyValuePath* parent, m_funcSetAlpha = registerFastName(L"SetControlAlpha"); m_nameVisible = registerFastName(L"visible"); - F64 fx, fy, fwidth, fheight; - IggyValueGetF64RS(getIggyValuePath(), m_nameXPos, NULL, &fx); - IggyValueGetF64RS(getIggyValuePath(), m_nameYPos, NULL, &fy); - IggyValueGetF64RS(getIggyValuePath(), m_nameWidth, NULL, &fwidth); - IggyValueGetF64RS(getIggyValuePath(), m_nameHeight, NULL, &fheight); + if (m_isValid) { + IggyDatatype controlType = IGGY_DATATYPE__invalid_request; + IggyResult typeResult = + IggyValueGetTypeRS(getIggyValuePath(), 0, NULL, &controlType); + m_isValid = typeResult == IGGY_RESULT_SUCCESS && + controlType != IGGY_DATATYPE__invalid_request && + controlType != IGGY_DATATYPE_undefined; + } - m_x = (S32)fx; - m_y = (S32)fy; - m_width = (S32)Math::round(fwidth); - m_height = (S32)Math::round(fheight); + if (m_isValid) { + F64 fx, fy, fwidth, fheight; + IggyValueGetF64RS(getIggyValuePath(), m_nameXPos, NULL, &fx); + IggyValueGetF64RS(getIggyValuePath(), m_nameYPos, NULL, &fy); + IggyValueGetF64RS(getIggyValuePath(), m_nameWidth, NULL, &fwidth); + IggyValueGetF64RS(getIggyValuePath(), m_nameHeight, NULL, &fheight); + + m_x = (S32)fx; + m_y = (S32)fy; + m_width = (S32)Math::round(fwidth); + m_height = (S32)Math::round(fheight); + } else { + m_x = 0; + m_y = 0; + m_width = 0; + m_height = 0; + } return res; } @@ -57,6 +79,8 @@ void UIControl::UpdateControl() { #endif // __PSVITA__ void UIControl::ReInit() { + if (!m_isValid) return; + if (m_lastOpacity != 1.0f) { IggyDataValue result; IggyDataValue value[2]; @@ -91,6 +115,7 @@ S32 UIControl::getHeight() { return m_height; } void UIControl::setOpacity(float percent) { if (percent != m_lastOpacity) { m_lastOpacity = percent; + if (!m_isValid) return; IggyDataValue result; IggyDataValue value[2]; @@ -112,6 +137,11 @@ void UIControl::setOpacity(float percent) { void UIControl::setVisible(bool visible) { if (visible != m_isVisible) { + if (!m_isValid) { + m_isVisible = visible; + return; + } + rrbool succ = IggyValueSetBooleanRS(getIggyValuePath(), m_nameVisible, NULL, visible); if (succ) @@ -122,6 +152,8 @@ void UIControl::setVisible(bool visible) { } bool UIControl::getVisible() { + if (!m_isValid) return m_isVisible; + rrbool bVisible = false; IggyResult result = IggyValueGetBooleanRS(getIggyValuePath(), m_nameVisible, diff --git a/Minecraft.Client/Platform/Common/UI/UIControl.h b/Minecraft.Client/Platform/Common/UI/UIControl.h index 19cdb9ad4..807daa5dd 100644 --- a/Minecraft.Client/Platform/Common/UI/UIControl.h +++ b/Minecraft.Client/Platform/Common/UI/UIControl.h @@ -34,6 +34,7 @@ protected: eUIControlType m_eControlType; int m_id; bool m_bHidden; // set by the Remove call + bool m_isValid; public: void setControlType(eUIControlType eType) { m_eControlType = eType; } @@ -83,6 +84,7 @@ public: void setVisible(bool visible); bool getVisible(); bool isVisible() { return m_isVisible; } + bool isValid() { return m_isValid; } virtual bool hasFocus() { return false; } diff --git a/Minecraft.Client/Platform/Common/UI/UIController.cpp b/Minecraft.Client/Platform/Common/UI/UIController.cpp index 912e827df..6ee65e8ec 100644 --- a/Minecraft.Client/Platform/Common/UI/UIController.cpp +++ b/Minecraft.Client/Platform/Common/UI/UIController.cpp @@ -77,6 +77,16 @@ static void RADLINK WarningCallback(void* user_callback_data, Iggy* player, // IGGY_RESULT_Error_UndefinedEntity = 504, // IGGY_RESULT_Error_OutOfMemory = 1001,}; + if (message != NULL) { + // 4jcraft: Some Linux movie variants do not ship these optional + // hooks/controls. We guard the call sites, so drop the residual Iggy + // warning noise. + if (strstr(message, "LabelGamertag") != NULL || + strstr(message, "Method SetSafeZone was not a function") != NULL) { + return; + } + } + switch (code) { case IGGY_RESULT_Warning_CannotSustainFrameRate: // Ignore warning @@ -3233,4 +3243,4 @@ void UIController::SendTouchInput(unsigned int iPad, unsigned int key, } } -#endif \ No newline at end of file +#endif diff --git a/Minecraft.Client/Platform/Common/UI/UIScene.cpp b/Minecraft.Client/Platform/Common/UI/UIScene.cpp index c50a82230..d46b97b37 100644 --- a/Minecraft.Client/Platform/Common/UI/UIScene.cpp +++ b/Minecraft.Client/Platform/Common/UI/UIScene.cpp @@ -19,6 +19,7 @@ UIScene::UIScene(int iPad, UILayer* parentLayer) { m_bVisible = true; m_bCanHandleInput = false; m_bIsReloading = false; + m_hasSetSafeZoneMethod = false; m_iFocusControl = -1; m_iFocusChild = 0; @@ -53,6 +54,7 @@ void UIScene::destroyMovie() { /* Destroy the Iggy player. */ IggyPlayerDestroy(swf); swf = NULL; + m_hasSetSafeZoneMethod = false; // Clear out the controls collection (doesn't delete the controls, and they // get re-setup later) @@ -76,6 +78,7 @@ void UIScene::reloadMovie(bool force) { // Clear out the controls collection (doesn't delete the controls, and // they get re-setup later) m_controls.clear(); + m_hasSetSafeZoneMethod = false; // Clear out all the fast names for the current movie m_fastNames.clear(); @@ -194,6 +197,8 @@ void UIScene::updateSafeZone() { void UIScene::setSafeZone(S32 safeTop, S32 safeBottom, S32 safeLeft, S32 safeRight) { + if (!m_hasSetSafeZoneMethod) return; + IggyDataValue result; IggyDataValue value[4]; @@ -251,6 +256,13 @@ bool UIScene::mapElementsAndNames() { m_funcSetAlpha = registerFastName(L"SetAlpha"); m_funcSetFocus = registerFastName(L"SetFocus"); m_funcHorizontalResizeCheck = registerFastName(L"DoHorizontalResizeCheck"); + + IggyDatatype safeZoneType = IGGY_DATATYPE__invalid_request; + IggyResult safeZoneResult = IggyValueGetTypeRS( + m_rootPath, m_funcSetSafeZone, NULL, &safeZoneType); + m_hasSetSafeZoneMethod = + safeZoneResult == IGGY_RESULT_SUCCESS && + safeZoneType == IGGY_DATATYPE_function; return true; } @@ -712,7 +724,7 @@ void UIScene::_customDrawSlotControl(CustomDrawData* region, int iPad, } glEnable(GL_RESCALE_NORMAL); glPushMatrix(); - Lighting::turnOnGui(); + Lighting::turnOn(); glRotatef(120, 1, 0, 0); glPopMatrix(); diff --git a/Minecraft.Client/Platform/Common/UI/UIScene.h b/Minecraft.Client/Platform/Common/UI/UIScene.h index b4948bfce..e7d746865 100644 --- a/Minecraft.Client/Platform/Common/UI/UIScene.h +++ b/Minecraft.Client/Platform/Common/UI/UIScene.h @@ -72,6 +72,7 @@ private: bool m_bUpdateOpacity; bool m_bVisible; bool m_bCanHandleInput; + bool m_hasSetSafeZoneMethod; UIScene* m_backScene; size_t m_callbackUniqueId; diff --git a/Minecraft.Client/Platform/Common/UI/UIScene_EnchantingMenu.cpp b/Minecraft.Client/Platform/Common/UI/UIScene_EnchantingMenu.cpp index 78fca71e5..0affa127f 100644 --- a/Minecraft.Client/Platform/Common/UI/UIScene_EnchantingMenu.cpp +++ b/Minecraft.Client/Platform/Common/UI/UIScene_EnchantingMenu.cpp @@ -5,8 +5,6 @@ #include "../../Minecraft.Client/Minecraft.h" #include "UIScene_EnchantingMenu.h" -#include - UIScene_EnchantingMenu::UIScene_EnchantingMenu(int iPad, void* _initData, UILayer* parentLayer) : UIScene_AbstractContainerMenu(iPad, parentLayer) { diff --git a/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.c b/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.c new file mode 100644 index 000000000..4fde9a91f --- /dev/null +++ b/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.c @@ -0,0 +1,835 @@ +#define GDRAW_ASSERTS + +#include "../../../Windows64/Iggy/include/iggy.h" +#include "../../../Windows64/Iggy/include/gdraw.h" +#include "gdraw.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define true 1 +#define false 0 + +#ifndef _ENABLEIGGY +void* IggyGDrawMallocAnnotated(SINTa size, const char* file, int line) { + (void)file; + (void)line; + return malloc((size_t)size); +} + +void IggyGDrawFree(void* ptr) { free(ptr); } + +void IggyGDrawSendWarning(Iggy* f, char const* message, ...) { + (void)f; + va_list args; + va_start(args, message); + fprintf(stderr, "[Iggy GDraw Warning] "); + vfprintf(stderr, message, args); + fprintf(stderr, "\n"); + va_end(args); +} + +void IggyDiscardVertexBufferCallback(void* owner, void* buf) { + (void)owner; + (void)buf; +} +#endif + +static void* get_gl_proc(const char* name) { + void* p = SDL_GL_GetProcAddress(name); + if (!p) p = dlsym(RTLD_DEFAULT, name); + if (!p) { + char buf[256]; + strncpy(buf, name, sizeof(buf) - 1); + buf[255] = '\0'; + char* ext = strstr(buf, "ARB"); + if (!ext) ext = strstr(buf, "EXT"); + if (ext && ext == buf + strlen(buf) - 3) { + *ext = '\0'; + p = SDL_GL_GetProcAddress(buf); + if (!p) p = dlsym(RTLD_DEFAULT, buf); + } + } + return p; +} + +#define GDRAW_GL_EXTENSION_LIST \ + /* identifier import procname */ \ + /* GL_ARB_vertex_buffer_object */ \ + GLE(GenBuffers, "GenBuffersARB", GENBUFFERSARB) \ + GLE(DeleteBuffers, "DeleteBuffersARB", DELETEBUFFERSARB) \ + GLE(BindBuffer, "BindBufferARB", BINDBUFFERARB) \ + GLE(BufferData, "BufferDataARB", BUFFERDATAARB) \ + GLE(MapBuffer, "MapBufferARB", MAPBUFFERARB) \ + GLE(UnmapBuffer, "UnmapBufferARB", UNMAPBUFFERARB) \ + GLE(VertexAttribPointer, "VertexAttribPointerARB", VERTEXATTRIBPOINTERARB) \ + GLE(EnableVertexAttribArray, "EnableVertexAttribArrayARB", \ + ENABLEVERTEXATTRIBARRAYARB) \ + GLE(DisableVertexAttribArray, "DisableVertexAttribArrayARB", \ + DISABLEVERTEXATTRIBARRAYARB) \ + /* GL_ARB_shader_objects */ \ + GLE(CreateShader, "CreateShaderObjectARB", CREATESHADEROBJECTARB) \ + GLE(DeleteShader, "DeleteObjectARB", DELETEOBJECTARB) \ + GLE(ShaderSource, "ShaderSourceARB", SHADERSOURCEARB) \ + GLE(CompileShader, "CompileShaderARB", COMPILESHADERARB) \ + GLE(GetShaderiv, "GetObjectParameterivARB", GETOBJECTPARAMETERIVARB) \ + GLE(GetShaderInfoLog, "GetInfoLogARB", GETINFOLOGARB) \ + GLE(CreateProgram, "CreateProgramObjectARB", CREATEPROGRAMOBJECTARB) \ + GLE(DeleteProgram, "DeleteObjectARB", DELETEOBJECTARB) \ + GLE(AttachShader, "AttachObjectARB", ATTACHOBJECTARB) \ + GLE(LinkProgram, "LinkProgramARB", LINKPROGRAMARB) \ + GLE(GetUniformLocation, "GetUniformLocationARB", GETUNIFORMLOCATIONARB) \ + GLE(UseProgram, "UseProgramObjectARB", USEPROGRAMOBJECTARB) \ + GLE(GetProgramiv, "GetObjectParameterivARB", GETOBJECTPARAMETERIVARB) \ + GLE(GetProgramInfoLog, "GetInfoLogARB", GETINFOLOGARB) \ + GLE(Uniform1i, "Uniform1iARB", UNIFORM1IARB) \ + GLE(Uniform4f, "Uniform4fARB", UNIFORM4FARB) \ + GLE(Uniform4fv, "Uniform4fvARB", UNIFORM4FVARB) \ + /* GL_ARB_vertex_shader */ \ + GLE(BindAttribLocation, "BindAttribLocationARB", BINDATTRIBLOCATIONARB) \ + /* Missing from WGL but needed by shared code */ \ + GLE(Uniform1f, "Uniform1fARB", UNIFORM1FARB) \ + /* GL_EXT_framebuffer_object */ \ + GLE(GenRenderbuffers, "GenRenderbuffersEXT", GENRENDERBUFFERSEXT) \ + GLE(DeleteRenderbuffers, "DeleteRenderbuffersEXT", DELETERENDERBUFFERSEXT) \ + GLE(BindRenderbuffer, "BindRenderbufferEXT", BINDRENDERBUFFEREXT) \ + GLE(RenderbufferStorage, "RenderbufferStorageEXT", RENDERBUFFERSTORAGEEXT) \ + GLE(GenFramebuffers, "GenFramebuffersEXT", GENFRAMEBUFFERSEXT) \ + GLE(DeleteFramebuffers, "DeleteFramebuffersEXT", DELETEFRAMEBUFFERSEXT) \ + GLE(BindFramebuffer, "BindFramebufferEXT", BINDFRAMEBUFFEREXT) \ + GLE(CheckFramebufferStatus, "CheckFramebufferStatusEXT", \ + CHECKFRAMEBUFFERSTATUSEXT) \ + GLE(FramebufferRenderbuffer, "FramebufferRenderbufferEXT", \ + FRAMEBUFFERRENDERBUFFEREXT) \ + GLE(FramebufferTexture2D, "FramebufferTexture2DEXT", \ + FRAMEBUFFERTEXTURE2DEXT) \ + GLE(GenerateMipmap, "GenerateMipmapEXT", GENERATEMIPMAPEXT) \ + /* GL_EXT_framebuffer_blit */ \ + GLE(BlitFramebuffer, "BlitFramebufferEXT", BLITFRAMEBUFFEREXT) \ + /* GL_EXT_framebuffer_multisample */ \ + GLE(RenderbufferStorageMultisample, "RenderbufferStorageMultisampleEXT", \ + RENDERBUFFERSTORAGEMULTISAMPLEEXT) \ + /* */ + +// Shared .inl +#define gdraw_GLx_(id) gdraw_GL_##id +#define GDRAW_GLx_(id) GDRAW_GL_##id +#define GDRAW_SHADERS "gdraw_gl_shaders.inl" + +// GLhandleARB is void* but shader functions use GLuint values. +// homework stolen from gdraw_gl_shared.inl. +#define GDrawGLProgram GLuint +typedef GLuint GLhandle; +typedef gdraw_gl_resourcetype gdraw_resourcetype; + +#define GLE(id, import, procname) static PFNGL##procname##PROC gl##id; +GDRAW_GL_EXTENSION_LIST +#undef GLE + +typedef const GLubyte*(APIENTRYP PFNGLGETSTRINGIPROC_)(GLenum name, + GLuint index); +static PFNGLGETSTRINGIPROC_ gdraw_glGetStringi = NULL; + +typedef void(APIENTRYP PFNGLGENVERTEXARRAYSPROC_)(GLsizei n, GLuint* arrays); +typedef void(APIENTRYP PFNGLBINDVERTEXARRAYPROC_)(GLuint array); +static PFNGLGENVERTEXARRAYSPROC_ gdraw_glGenVertexArrays = NULL; +static PFNGLBINDVERTEXARRAYPROC_ gdraw_glBindVertexArray = NULL; +static GLuint gdraw_vao = 0; + +typedef void(APIENTRYP gdraw_vtxattrib_fn)(GLuint, GLint, GLenum, GLboolean, + GLsizei, const void*); +static gdraw_vtxattrib_fn gdraw_real_vtxattrib = NULL; +static GLuint gdraw_screenvbo = 0; +static const void* gdraw_screenvbo_base = NULL; +static size_t gdraw_expected_vbo_size = 0; + +typedef void(APIENTRYP gdraw_drawelements_fn)(GLenum mode, GLsizei count, + GLenum type, const void* indices); +static gdraw_drawelements_fn gdraw_real_drawelements = NULL; +static GLuint gdraw_screenibo = 0; + +typedef GLuint(APIENTRYP gdraw_createshader_fn)(GLenum); +typedef void(APIENTRYP gdraw_shadersource_fn)(GLuint, GLsizei, const GLchar**, + const GLint*); +typedef void(APIENTRYP gdraw_compileshader_fn)(GLuint); +typedef void(APIENTRYP gdraw_linkprogram_fn)(GLuint); +static gdraw_createshader_fn gdraw_real_createshader = NULL; +static gdraw_shadersource_fn gdraw_real_shadersource = NULL; +static gdraw_compileshader_fn gdraw_real_compileshader = NULL; +static gdraw_linkprogram_fn gdraw_real_linkprogram = NULL; + +// some core reject p0 + +typedef void(APIENTRYP gdraw_useprogram_fn)(GLuint); +static gdraw_useprogram_fn gdraw_real_useprogram = NULL; +static GLuint gdraw_null_program = 0; + +typedef void(APIENTRYP gdraw_teximage2d_fn)(GLenum, GLint, GLint, GLsizei, + GLsizei, GLint, GLenum, GLenum, + const void*); +typedef void(APIENTRYP gdraw_texsubimage2d_fn)(GLenum, GLint, GLint, GLint, + GLsizei, GLsizei, GLenum, GLenum, + const void*); +static gdraw_teximage2d_fn gdraw_real_teximage2d = NULL; +static gdraw_texsubimage2d_fn gdraw_real_texsubimage2d = NULL; + +#define TRY(ptr, arb, core) \ + do { \ + void* _p = get_gl_proc(core); \ + if (!_p) _p = get_gl_proc(arb); \ + *(void**)&(ptr) = _p; \ + } while (0) + +static void load_extensions(void) { +// gl_shared requires ts shit ugh +#define GLE(id, import, procname) \ + gl##id = (PFNGL##procname##PROC)get_gl_proc("gl" import); + GDRAW_GL_EXTENSION_LIST +#undef GLE + + TRY(glCreateShader, "glCreateShaderObjectARB", "glCreateShader"); + TRY(glDeleteShader, "glDeleteObjectARB", "glDeleteShader"); + TRY(glShaderSource, "glShaderSourceARB", "glShaderSource"); + TRY(glCompileShader, "glCompileShaderARB", "glCompileShader"); + TRY(glGetShaderiv, "glGetObjectParameterivARB", "glGetShaderiv"); + TRY(glGetShaderInfoLog, "glGetInfoLogARB", "glGetShaderInfoLog"); + TRY(glCreateProgram, "glCreateProgramObjectARB", "glCreateProgram"); + TRY(glDeleteProgram, "glDeleteObjectARB", "glDeleteProgram"); + TRY(glAttachShader, "glAttachObjectARB", "glAttachShader"); + TRY(glLinkProgram, "glLinkProgramARB", "glLinkProgram"); + TRY(glGetUniformLocation, "glGetUniformLocationARB", + "glGetUniformLocation"); + TRY(glUseProgram, "glUseProgramObjectARB", "glUseProgram"); + TRY(glGetProgramiv, "glGetObjectParameterivARB", "glGetProgramiv"); + TRY(glGetProgramInfoLog, "glGetInfoLogARB", "glGetProgramInfoLog"); + TRY(glUniform1i, "glUniform1iARB", "glUniform1i"); + TRY(glUniform4f, "glUniform4fARB", "glUniform4f"); + TRY(glUniform4fv, "glUniform4fvARB", "glUniform4fv"); + TRY(glUniform1f, "glUniform1fARB", "glUniform1f"); + TRY(glBindAttribLocation, "glBindAttribLocationARB", + "glBindAttribLocation"); + + TRY(glGenBuffers, "glGenBuffersARB", "glGenBuffers"); + TRY(glDeleteBuffers, "glDeleteBuffersARB", "glDeleteBuffers"); + TRY(glBindBuffer, "glBindBufferARB", "glBindBuffer"); + TRY(glBufferData, "glBufferDataARB", "glBufferData"); + TRY(glMapBuffer, "glMapBufferARB", "glMapBuffer"); + TRY(glUnmapBuffer, "glUnmapBufferARB", "glUnmapBuffer"); + TRY(glVertexAttribPointer, "glVertexAttribPointerARB", + "glVertexAttribPointer"); + TRY(glEnableVertexAttribArray, "glEnableVertexAttribArrayARB", + "glEnableVertexAttribArray"); + TRY(glDisableVertexAttribArray, "glDisableVertexAttribArrayARB", + "glDisableVertexAttribArray"); + + TRY(glGenRenderbuffers, "glGenRenderbuffersEXT", "glGenRenderbuffers"); + TRY(glDeleteRenderbuffers, "glDeleteRenderbuffersEXT", + "glDeleteRenderbuffers"); + TRY(glBindRenderbuffer, "glBindRenderbufferEXT", "glBindRenderbuffer"); + TRY(glRenderbufferStorage, "glRenderbufferStorageEXT", + "glRenderbufferStorage"); + TRY(glGenFramebuffers, "glGenFramebuffersEXT", "glGenFramebuffers"); + TRY(glDeleteFramebuffers, "glDeleteFramebuffersEXT", + "glDeleteFramebuffers"); + TRY(glBindFramebuffer, "glBindFramebufferEXT", "glBindFramebuffer"); + TRY(glCheckFramebufferStatus, "glCheckFramebufferStatusEXT", + "glCheckFramebufferStatus"); + TRY(glFramebufferRenderbuffer, "glFramebufferRenderbufferEXT", + "glFramebufferRenderbuffer"); + TRY(glFramebufferTexture2D, "glFramebufferTexture2DEXT", + "glFramebufferTexture2D"); + TRY(glGenerateMipmap, "glGenerateMipmapEXT", "glGenerateMipmap"); + TRY(glBlitFramebuffer, "glBlitFramebufferEXT", "glBlitFramebuffer"); + TRY(glRenderbufferStorageMultisample, "glRenderbufferStorageMultisampleEXT", + "glRenderbufferStorageMultisample"); + + // Save raw pointers before we #define over the names below + gdraw_real_vtxattrib = + (gdraw_vtxattrib_fn)get_gl_proc("glVertexAttribPointer"); + gdraw_real_createshader = + (gdraw_createshader_fn)get_gl_proc("glCreateShader"); + gdraw_real_shadersource = + (gdraw_shadersource_fn)get_gl_proc("glShaderSource"); + gdraw_real_compileshader = + (gdraw_compileshader_fn)get_gl_proc("glCompileShader"); + gdraw_real_linkprogram = (gdraw_linkprogram_fn)get_gl_proc("glLinkProgram"); + gdraw_real_teximage2d = (gdraw_teximage2d_fn)get_gl_proc("glTexImage2D"); + gdraw_real_texsubimage2d = + (gdraw_texsubimage2d_fn)get_gl_proc("glTexSubImage2D"); + gdraw_real_useprogram = (gdraw_useprogram_fn)get_gl_proc("glUseProgram"); + gdraw_real_drawelements = + (gdraw_drawelements_fn)get_gl_proc("glDrawElements"); + + gdraw_glGetStringi = (PFNGLGETSTRINGIPROC_)get_gl_proc("glGetStringi"); + gdraw_glGenVertexArrays = + (PFNGLGENVERTEXARRAYSPROC_)get_gl_proc("glGenVertexArrays"); + gdraw_glBindVertexArray = + (PFNGLBINDVERTEXARRAYPROC_)get_gl_proc("glBindVertexArray"); + + if (gdraw_glGenVertexArrays && gdraw_glBindVertexArray && gdraw_vao == 0) { + gdraw_glGenVertexArrays(1, &gdraw_vao); + gdraw_glBindVertexArray(gdraw_vao); + } +} + +#undef TRY + +// rebind vbo + +static void clear_renderstate_platform_specific(void) { + if (gdraw_glBindVertexArray && gdraw_vao) + gdraw_glBindVertexArray(gdraw_vao); +} + +static void error_msg_platform_specific(const char* msg) { + fprintf(stderr, "[GDraw] %s\n", msg); +} + +#define GDRAW_PLATFORM_REPORT_GL_SITE(site) \ + do { \ + if ((site) != NULL) \ + fprintf(stderr, "[GDraw] GL error site: %s\n", (site)); \ + } while (0) + +#define GDRAW_MULTISAMPLING + +// i wish i could improve this function +#ifdef RR_BREAK +#undef RR_BREAK +#endif +#define RR_BREAK() \ + do { \ + fprintf(stderr, "[GDraw] GL error at %s:%d\n", __FILE__, __LINE__); \ + } while (0) + +// the magic number that tropical told me +#define GDRAW_MAX_SHADERS 64 +static struct { + GLuint handle; + GLenum type; +} gdraw_shader_types[GDRAW_MAX_SHADERS]; +static int gdraw_shader_type_count = 0; + +static GLenum gdraw_get_shader_type(GLuint shader) { + for (int i = 0; i < gdraw_shader_type_count; i++) + if (gdraw_shader_types[i].handle == shader) + return gdraw_shader_types[i].type; + return GL_FRAGMENT_SHADER; +} + +static GLuint gdraw_CreateShaderTracked(GLenum type) { + GLuint h = gdraw_real_createshader(type); + if (h && gdraw_shader_type_count < GDRAW_MAX_SHADERS) { + gdraw_shader_types[gdraw_shader_type_count].handle = h; + gdraw_shader_types[gdraw_shader_type_count].type = type; + gdraw_shader_type_count++; + } + return h; +} + +static void gdraw_CompileShaderAndLog(GLuint shader) { + GLint status = 0; + gdraw_real_compileshader(shader); + glGetShaderiv(shader, GL_COMPILE_STATUS, &status); + if (!status) { + char log[2048]; + GLint len = 0; + glGetShaderInfoLog(shader, (GLsizei)sizeof(log) - 1, &len, log); + log[len] = '\0'; + fprintf(stderr, "[GDraw GLSL] compile FAILED shader=%u:\n%s\n", shader, + log); + } +} + +static void gdraw_LinkProgramAndLog(GLuint program) { + GLint status = 0; + gdraw_real_linkprogram(program); + glGetProgramiv(program, GL_LINK_STATUS, &status); + if (!status) { + char log[2048]; + GLint len = 0; + glGetProgramInfoLog(program, (GLsizei)sizeof(log) - 1, &len, log); + log[len] = '\0'; + fprintf(stderr, "[GDraw GLSL] link FAILED program=%u:\n%s\n", program, + log); + } +} + +#undef glCreateShader +#define glCreateShader gdraw_CreateShaderTracked + +// This is the part that turns the old ugly shaders to 330 +static char* gdraw_strreplace(char* src, const char* find, const char* rep) { + char* result; + char* pos; + char* base = src; + size_t find_len = strlen(find); + size_t rep_len = strlen(rep); + size_t count = 0; + char* tmp = src; + + while ((tmp = strstr(tmp, find))) { + count++; + tmp += find_len; + } + if (!count) return src; + + result = (char*)malloc(strlen(src) + count * (rep_len + 1) + 1); + if (!result) return src; + + tmp = result; + while ((pos = strstr(src, find))) { + size_t before = (size_t)(pos - src); + memcpy(tmp, src, before); + tmp += before; + memcpy(tmp, rep, rep_len); + tmp += rep_len; + src = pos + find_len; + } + strcpy(tmp, src); + free(base); + return result; +} + +static void gdraw_ShaderSourceUpgraded(GLuint shader, GLsizei count, + const GLchar** strings, + const GLint* lengths) { + size_t total = 0; + for (int i = 0; i < count; i++) + total += lengths ? (lengths[i] >= 0 ? (size_t)lengths[i] + : strlen(strings[i])) + : strlen(strings[i]); + + char* src = (char*)malloc(total + 1); + if (!src) { + gdraw_real_shadersource(shader, count, strings, lengths); + return; + } + + src[0] = '\0'; + for (int i = 0; i < count; i++) { + size_t len = lengths ? (lengths[i] >= 0 ? (size_t)lengths[i] + : strlen(strings[i])) + : strlen(strings[i]); + strncat(src, strings[i], len); + } + + int is_vert = (gdraw_get_shader_type(shader) == GL_VERTEX_SHADER); + + // Strip any existing #version directive as i'll add our own + { + char* vp = strstr(src, "#version"); + if (vp) { + char* nl = strchr(vp, '\n'); + if (nl) + memmove(vp, nl + 1, strlen(nl + 1) + 1); + else + *vp = '\0'; + } + } + + // Texture built-ins + src = gdraw_strreplace(src, "texture2DRect", "texture"); + src = gdraw_strreplace(src, "texture2D", "texture"); + + // Attribute -> in + src = gdraw_strreplace(src, "attribute ", "in "); + src = gdraw_strreplace(src, "attribute\t", "in\t"); + src = gdraw_strreplace(src, "attribute\n", "in\n"); + + // Varying -> out (vert) / in (frag) + if (is_vert) { + src = gdraw_strreplace(src, "varying ", "out "); + src = gdraw_strreplace(src, "varying\t", "out\t"); + src = gdraw_strreplace(src, "varying\n", "out\n"); + } else { + src = gdraw_strreplace(src, "varying ", "in "); + src = gdraw_strreplace(src, "varying\t", "in\t"); + src = gdraw_strreplace(src, "varying\n", "in\n"); + src = gdraw_strreplace(src, "gl_FragData[0]", "_gdraw_frag_out"); + src = gdraw_strreplace(src, "gl_FragColor", "_gdraw_frag_out"); + } + + const char* header = is_vert + ? "#version 330 core\n" + : "#version 330 core\nout vec4 _gdraw_frag_out;\n"; + char* patched = (char*)malloc(strlen(header) + strlen(src) + 2); + if (!patched) { + free(src); + gdraw_real_shadersource(shader, count, strings, lengths); + return; + } + strcpy(patched, header); + strcat(patched, src); + free(src); + + const GLchar* patched_ptr = (const GLchar*)patched; + gdraw_real_shadersource(shader, 1, &patched_ptr, NULL); + free(patched); +} + +#undef glShaderSource +#define glShaderSource gdraw_ShaderSourceUpgraded + +// Remap all the deprecated internal formats to their modern equivalents +// (idk why but just the word "swizzle" is cracking me up) +static void gdraw_apply_swizzle(GLenum internal_fmt) { + if (internal_fmt == 0x1906 /* GL_ALPHA */ || internal_fmt == GL_RED) { + GLint sw[4] = {GL_ZERO, GL_ZERO, GL_ZERO, GL_RED}; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, sw); + } else if (internal_fmt == 0x1909 /* GL_LUMINANCE */) { + GLint sw[4] = {GL_RED, GL_RED, GL_RED, GL_ONE}; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, sw); + } else if (internal_fmt == 0x190A /* GL_LUMINANCE_ALPHA */) { + GLint sw[4] = {GL_RED, GL_RED, GL_RED, GL_GREEN}; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, sw); + } +} + +static GLenum gdraw_remap_fmt(GLenum fmt) { + switch (fmt) { + case 0x1906: + return GL_RED; // GL_ALPHA + case 0x1909: + return GL_RED; // GL_LUMINANCE + case 0x190A: + return GL_RG; // GL_LUMINANCE_ALPHA + case 0x8033: + return GL_RG; // GL_LUMINANCE4_ALPHA4 + case 0x8045: + return GL_R8; // GL_LUMINANCE8 + case 0x8048: + return GL_RG8; // GL_LUMINANCE8_ALPHA8 + case 0x804F: + return GL_R8; // GL_INTENSITY4 + case 0x8050: + return GL_R8; // GL_INTENSITY8 + default: + return fmt; + } +} + +static void gdraw_TexImage2D(GLenum target, GLint level, GLint ifmt, GLsizei w, + GLsizei h, GLint border, GLenum fmt, GLenum type, + const void* data) { + // ES strictly requires explicitly sized formats & stuff + if (ifmt == GL_RGBA && data == NULL) ifmt = GL_RGBA8; + + GLenum new_ifmt = gdraw_remap_fmt((GLenum)ifmt); + GLenum new_fmt = gdraw_remap_fmt(fmt); + gdraw_real_teximage2d(target, level, (GLint)new_ifmt, w, h, border, new_fmt, + type, data); + if (new_ifmt != (GLenum)ifmt) gdraw_apply_swizzle((GLenum)ifmt); +} + +static void gdraw_TexSubImage2D(GLenum target, GLint level, GLint xoff, + GLint yoff, GLsizei w, GLsizei h, GLenum fmt, + GLenum type, const void* data) { + GLenum new_fmt = gdraw_remap_fmt(fmt); + gdraw_real_texsubimage2d(target, level, xoff, yoff, w, h, new_fmt, type, + data); +} + +#undef glTexImage2D +#define glTexImage2D gdraw_TexImage2D +#undef glTexSubImage2D +#define glTexSubImage2D gdraw_TexSubImage2D + +// vbo emu +static void gdraw_ClientVertexAttribPointer(GLuint index, GLint size, + GLenum type, GLboolean normalized, + GLsizei stride, + const void* pointer) { + if (gdraw_glBindVertexArray && gdraw_vao) { + GLint current_vao = 0; + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, ¤t_vao); + if ((GLuint)current_vao != gdraw_vao) + gdraw_glBindVertexArray(gdraw_vao); + } + + GLint current_vbo = 0; + glGetIntegerv(GL_ARRAY_BUFFER_BINDING, ¤t_vbo); + + if (current_vbo != 0 && current_vbo != (GLint)gdraw_screenvbo) { + // no touchies + gdraw_real_vtxattrib(index, size, type, normalized, stride, pointer); + return; + } + + if (pointer == NULL) { + gdraw_real_vtxattrib(index, size, type, normalized, stride, pointer); + return; + } + + ptrdiff_t offset = + gdraw_screenvbo_base + ? ((const char*)pointer - (const char*)gdraw_screenvbo_base) + : -1; + + if (gdraw_screenvbo_base == NULL || offset < 0 || + offset >= (ptrdiff_t)gdraw_expected_vbo_size) { + if (!gdraw_screenvbo) glGenBuffers(1, &gdraw_screenvbo); + glBindBuffer(GL_ARRAY_BUFFER, gdraw_screenvbo); + + size_t upload_size = gdraw_expected_vbo_size > 0 + ? (gdraw_expected_vbo_size + 256) + : 65536; + glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)upload_size, pointer, + GL_STREAM_DRAW); + + gdraw_screenvbo_base = pointer; + gdraw_real_vtxattrib(index, size, type, normalized, stride, + (const void*)0); + } else { + glBindBuffer(GL_ARRAY_BUFFER, gdraw_screenvbo); + gdraw_real_vtxattrib(index, size, type, normalized, stride, + (const void*)offset); + } +} + +#undef glVertexAttribPointer +#define glVertexAttribPointer gdraw_ClientVertexAttribPointer + +// fake ibo +static void hooked_glDrawElements(GLenum mode, GLsizei count, GLenum type, + const void* indices) { + GLint current_ibo = 0; + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, ¤t_ibo); + + if (current_ibo == 0 && indices != NULL) { + if (!gdraw_screenibo) glGenBuffers(1, &gdraw_screenibo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gdraw_screenibo); + + size_t index_size = (type == GL_UNSIGNED_SHORT) ? 2 + : (type == GL_UNSIGNED_BYTE) ? 1 + : 4; + glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)(count * index_size), + indices, GL_STREAM_DRAW); + + gdraw_real_drawelements(mode, count, type, (const void*)0); + } else { + gdraw_real_drawelements(mode, count, type, indices); + } +} + +#define glDrawElements hooked_glDrawElements + +// dummy shader for glUseProgram(0) safety +static void gdraw_UseProgramSafe(GLuint program) { + if (!program) { + if (!gdraw_null_program && gdraw_real_useprogram) { + const char* vs = + "#version 330 core\nvoid main(){gl_Position=vec4(0);}"; + const char* fs = + "#version 330 core\nout vec4 c;\nvoid main(){c=vec4(0);}"; + GLuint v = gdraw_real_createshader(GL_VERTEX_SHADER); + GLuint f = gdraw_real_createshader(GL_FRAGMENT_SHADER); + gdraw_real_shadersource(v, 1, &vs, NULL); + gdraw_real_shadersource(f, 1, &fs, NULL); + gdraw_real_compileshader(v); + gdraw_real_compileshader(f); + gdraw_null_program = glCreateProgram(); + glAttachShader(gdraw_null_program, v); + glAttachShader(gdraw_null_program, f); + gdraw_real_linkprogram(gdraw_null_program); + glDeleteShader(v); + glDeleteShader(f); + } + gdraw_real_useprogram(gdraw_null_program); + return; + } + gdraw_real_useprogram(program); +} + +#undef glUseProgram +#define glUseProgram gdraw_UseProgramSafe +#undef glCompileShader +#define glCompileShader gdraw_CompileShaderAndLog +#undef glLinkProgram +#define glLinkProgram gdraw_LinkProgramAndLog + +static void gdraw_FramebufferRenderbufferSafe(GLenum target, GLenum attachment, + GLenum renderbuffertarget, + GLuint renderbuffer) { + static GLuint last_depth_rb = 0; + + if (attachment == GL_DEPTH_ATTACHMENT) { + last_depth_rb = renderbuffer; + (glFramebufferRenderbuffer)(target, attachment, renderbuffertarget, + renderbuffer); + } else if (attachment == GL_STENCIL_ATTACHMENT) { + if (renderbuffer == last_depth_rb && renderbuffer != 0) { + // If identical, bind as packed depth-stencil to satisfy strict GLES + // ^ how greedy -n- + (glFramebufferRenderbuffer)( + target, 0x821A /* GL_DEPTH_STENCIL_ATTACHMENT */, + renderbuffertarget, renderbuffer); + } else { + (glFramebufferRenderbuffer)(target, attachment, renderbuffertarget, + renderbuffer); + } + } else { + (glFramebufferRenderbuffer)(target, attachment, renderbuffertarget, + renderbuffer); + } +} +#define glFramebufferRenderbuffer_SAFE gdraw_FramebufferRenderbufferSafe +#define glFramebufferRenderbuffer glFramebufferRenderbuffer_SAFE + +#include "../../../Windows64/Iggy/gdraw/gdraw_gl_shared.inl" +#undef glVertexAttribPointer +#define glVertexAttribPointer gdraw_real_vtxattrib + +static int hasext_core(const char* name) { + GLint n = 0; + if (!gdraw_glGetStringi) return 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &n); + for (GLint i = 0; i < n; i++) { + const char* e = + (const char*)gdraw_glGetStringi(GL_EXTENSIONS, (GLuint)i); + if (e && strcmp(e, name) == 0) return 1; + } + return 0; +} + +static gdraw_draw_indexed_triangles* real_DrawIndexedTriangles = NULL; + +static void RADLINK hooked_DrawIndexedTriangles(GDrawRenderState* r, + GDrawPrimitive* prim, + GDrawVertexBuffer* buf, + GDrawStats* stats) { + if (buf == NULL && prim != NULL && prim->vertices != NULL) { + size_t stride = 8; + if (prim->vertex_format == GDRAW_vformat_v2aa) + stride = 16; + else if (prim->vertex_format == GDRAW_vformat_v2tc2) + stride = 16; + else if (prim->vertex_format == GDRAW_vformat_ihud1) + stride = 20; + gdraw_expected_vbo_size = prim->num_vertices * stride; + } else { + gdraw_expected_vbo_size = 0; + } + gdraw_screenvbo_base = NULL; // Force VBO re-upload for each primitive + real_DrawIndexedTriangles(r, prim, buf, stats); +} + +static gdraw_filter_quad* real_FilterQuad = NULL; + +static void RADLINK hooked_FilterQuad(GDrawRenderState* r, S32 x0, S32 y0, + S32 x1, S32 y1, GDrawStats* stats) { + gdraw_expected_vbo_size = 4 * 20; // 4 vertices, max stride + gdraw_screenvbo_base = NULL; + real_FilterQuad(r, x0, y0, x1, y1, stats); +} + +static gdraw_rendering_begin* real_RenderingBegin = NULL; + +// stupid hack +static void RADLINK hooked_RenderingBegin(void) { + if (real_RenderingBegin) real_RenderingBegin(); + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + OPENGL_CHECK_SITE("hooked_RenderingBegin:post_state"); +} + +// Creating the context +GDrawFunctions* gdraw_GL_CreateContext(S32 w, S32 h, S32 msaa_samples) { + static const TextureFormatDesc tex_formats[] = { + {IFT_FORMAT_rgba_8888, 1, 1, 4, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE}, + {IFT_FORMAT_rgba_4444_LE, 1, 1, 2, GL_RGBA4, GL_RGBA, + GL_UNSIGNED_SHORT_4_4_4_4}, + {IFT_FORMAT_rgba_5551_LE, 1, 1, 2, GL_RGB5_A1, GL_RGBA, + GL_UNSIGNED_SHORT_5_5_5_1}, + {IFT_FORMAT_la_88, 1, 1, 2, GL_LUMINANCE8_ALPHA8, GL_LUMINANCE_ALPHA, + GL_UNSIGNED_BYTE}, + {IFT_FORMAT_la_44, 1, 1, 1, GL_LUMINANCE4_ALPHA4, GL_LUMINANCE_ALPHA, + GL_UNSIGNED_BYTE}, + {IFT_FORMAT_i_8, 1, 1, 1, GL_INTENSITY8, GL_ALPHA, GL_UNSIGNED_BYTE}, + {IFT_FORMAT_i_4, 1, 1, 1, GL_INTENSITY4, GL_ALPHA, GL_UNSIGNED_BYTE}, + {IFT_FORMAT_l_8, 1, 1, 1, GL_LUMINANCE8, GL_LUMINANCE, + GL_UNSIGNED_BYTE}, + {IFT_FORMAT_l_4, 1, 1, 1, GL_LUMINANCE4, GL_LUMINANCE, + GL_UNSIGNED_BYTE}, + {IFT_FORMAT_DXT1, 4, 4, 8, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0, + GL_UNSIGNED_BYTE}, + {IFT_FORMAT_DXT3, 4, 4, 16, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0, + GL_UNSIGNED_BYTE}, + {IFT_FORMAT_DXT5, 4, 4, 16, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0, + GL_UNSIGNED_BYTE}, + {0, 0, 0, 0, 0, 0, 0}, + }; + + GLint major = 0, minor = 0; + glGetIntegerv(GL_MAJOR_VERSION, &major); + glGetIntegerv(GL_MINOR_VERSION, &minor); + if (major < 3) { + fprintf(stderr, "[GDraw] GL 3.0 or higher required (got %d.%d)\n", + major, minor); + return NULL; + } + + load_extensions(); + + if (gdraw_glBindVertexArray && gdraw_vao) + gdraw_glBindVertexArray(gdraw_vao); + + GDrawFunctions* funcs = create_context(w, h); + if (!funcs) return NULL; + + // hook the vtable entries for VBO reset and render state + real_DrawIndexedTriangles = funcs->DrawIndexedTriangles; + funcs->DrawIndexedTriangles = hooked_DrawIndexedTriangles; + + real_FilterQuad = funcs->FilterQuad; + funcs->FilterQuad = hooked_FilterQuad; + + real_RenderingBegin = funcs->RenderingBegin; + funcs->RenderingBegin = hooked_RenderingBegin; + funcs->ClearID = gdraw_ClearID; + + gdraw->tex_formats = tex_formats; + gdraw->has_mapbuffer = false; + gdraw->has_depth24 = true; + gdraw->has_texture_max_level = true; + + gdraw->has_packed_depth_stencil = true; + + GLint n = 0; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &n); + gdraw->has_conditional_non_power_of_two = (n < 8192); + + if (msaa_samples > 1) { + glGetIntegerv(GL_MAX_SAMPLES, &n); + gdraw->multisampling = RR_MIN(msaa_samples, n); + } + + opengl_check(); + fprintf(stderr, "[GDraw] Context created successfully (%dx%d, msaa=%d)\n", + w, h, msaa_samples); + return funcs; +} + +// Custom draw callbacks +void gdraw_GL_BeginCustomDraw_4J(IggyCustomDrawCallbackRegion* region, + F32* matrix) { + // rebind vbo + if (gdraw_glBindVertexArray && gdraw_vao) + gdraw_glBindVertexArray(gdraw_vao); + clear_renderstate(); + gdraw_GetObjectSpaceMatrix(matrix, region->o2w, gdraw->projection, + depth_from_id(0), 0); +} + +void gdraw_GL_CalculateCustomDraw_4J(IggyCustomDrawCallbackRegion* region, + F32* matrix) { + gdraw_GetObjectSpaceMatrix(matrix, region->o2w, gdraw->projection, 0.0f, 0); +} diff --git a/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw_sdl.h b/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.h similarity index 94% rename from Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw_sdl.h rename to Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.h index 063660297..326d5c5a5 100644 --- a/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw_sdl.h +++ b/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw.h @@ -1,7 +1,9 @@ -#ifndef __RAD_INCLUDE_GDRAW_SDL_H__ -#define __RAD_INCLUDE_GDRAW_SDL_H__ +#ifndef __LINUX_IGGY_GDRAW_H__ +#define __LINUX_IGGY_GDRAW_H__ + #include "../../../Windows64/Iggy/include/gdraw.h" #include "../../../Windows64/Iggy/include/iggy.h" + #ifdef __cplusplus extern "C" { #endif @@ -34,4 +36,5 @@ extern void gdraw_GL_DestroyTextureFromResource(GDrawTexture *tex); #ifdef __cplusplus } #endif -#endif // __RAD_INCLUDE_GDRAW_SDL_H__ \ No newline at end of file + +#endif // __LINUX_IGGY_GDRAW_H__ \ No newline at end of file diff --git a/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw_sdl.c b/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw_sdl.c deleted file mode 100644 index 7e3506367..000000000 --- a/Minecraft.Client/Platform/Linux/Iggy/gdraw/gdraw_sdl.c +++ /dev/null @@ -1,240 +0,0 @@ -// Rewrite of gdraw_GLFW to gdraw_SDL -// I hope iggy gets fully implemented rrlllly quickly <3 -#define GDRAW_ASSERTS - -#include "../../../Windows64/Iggy/include/iggy.h" -#include "../../../Windows64/Iggy/include/gdraw.h" -#include "gdraw_sdl.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define true 1 -#define false 0 - -// Say hi to sdl - -#ifndef _ENABLEIGGY -void *IggyGDrawMallocAnnotated(SINTa size, const char *file, int line) -{ - (void)file; (void)line; - return malloc((size_t)size); -} - -void IggyGDrawFree(void *ptr) -{ - free(ptr); -} - -void IggyGDrawSendWarning(Iggy *f, char const *message, ...) -{ - (void)f; - va_list args; - va_start(args, message); - fprintf(stderr, "[Iggy GDraw Warning] "); - vfprintf(stderr, message, args); - fprintf(stderr, "\n"); - va_end(args); -} - -void IggyDiscardVertexBufferCallback(void *owner, void *buf) -{ - (void)owner; (void)buf; -} -#endif - -// glActiveTexture and glCompressedTexImage2D are core GL 1.3+ on Linux and -// are declared directly in , so they are omitted from this list. -#define GDRAW_GL_EXTENSION_LIST \ -/* identifier import procname */ \ -/* GL_ARB_vertex_buffer_object */ \ -GLE(GenBuffers, "GenBuffersARB", GENBUFFERSARB) \ -GLE(DeleteBuffers, "DeleteBuffersARB", DELETEBUFFERSARB) \ -GLE(BindBuffer, "BindBufferARB", BINDBUFFERARB) \ -GLE(BufferData, "BufferDataARB", BUFFERDATAARB) \ -GLE(MapBuffer, "MapBufferARB", MAPBUFFERARB) \ -GLE(UnmapBuffer, "UnmapBufferARB", UNMAPBUFFERARB) \ -GLE(VertexAttribPointer, "VertexAttribPointerARB", VERTEXATTRIBPOINTERARB) \ -GLE(EnableVertexAttribArray, "EnableVertexAttribArrayARB", ENABLEVERTEXATTRIBARRAYARB) \ -GLE(DisableVertexAttribArray, "DisableVertexAttribArrayARB", DISABLEVERTEXATTRIBARRAYARB) \ -/* GL_ARB_shader_objects */ \ -GLE(CreateShader, "CreateShaderObjectARB", CREATESHADEROBJECTARB) \ -GLE(DeleteShader, "DeleteObjectARB", DELETEOBJECTARB) \ -GLE(ShaderSource, "ShaderSourceARB", SHADERSOURCEARB) \ -GLE(CompileShader, "CompileShaderARB", COMPILESHADERARB) \ -GLE(GetShaderiv, "GetObjectParameterivARB", GETOBJECTPARAMETERIVARB) \ -GLE(GetShaderInfoLog, "GetInfoLogARB", GETINFOLOGARB) \ -GLE(CreateProgram, "CreateProgramObjectARB", CREATEPROGRAMOBJECTARB) \ -GLE(DeleteProgram, "DeleteObjectARB", DELETEOBJECTARB) \ -GLE(AttachShader, "AttachObjectARB", ATTACHOBJECTARB) \ -GLE(LinkProgram, "LinkProgramARB", LINKPROGRAMARB) \ -GLE(GetUniformLocation, "GetUniformLocationARB", GETUNIFORMLOCATIONARB) \ -GLE(UseProgram, "UseProgramObjectARB", USEPROGRAMOBJECTARB) \ -GLE(GetProgramiv, "GetObjectParameterivARB", GETOBJECTPARAMETERIVARB) \ -GLE(GetProgramInfoLog, "GetInfoLogARB", GETINFOLOGARB) \ -GLE(Uniform1i, "Uniform1iARB", UNIFORM1IARB) \ -GLE(Uniform4f, "Uniform4fARB", UNIFORM4FARB) \ -GLE(Uniform4fv, "Uniform4fvARB", UNIFORM4FVARB) \ -/* GL_ARB_vertex_shader */ \ -GLE(BindAttribLocation, "BindAttribLocationARB", BINDATTRIBLOCATIONARB) \ -/* Missing from WGL but needed by shared code */ \ -GLE(Uniform1f, "Uniform1fARB", UNIFORM1FARB) \ -/* GL_EXT_framebuffer_object */ \ -GLE(GenRenderbuffers, "GenRenderbuffersEXT", GENRENDERBUFFERSEXT) \ -GLE(DeleteRenderbuffers, "DeleteRenderbuffersEXT", DELETERENDERBUFFERSEXT) \ -GLE(BindRenderbuffer, "BindRenderbufferEXT", BINDRENDERBUFFEREXT) \ -GLE(RenderbufferStorage, "RenderbufferStorageEXT", RENDERBUFFERSTORAGEEXT) \ -GLE(GenFramebuffers, "GenFramebuffersEXT", GENFRAMEBUFFERSEXT) \ -GLE(DeleteFramebuffers, "DeleteFramebuffersEXT", DELETEFRAMEBUFFERSEXT) \ -GLE(BindFramebuffer, "BindFramebufferEXT", BINDFRAMEBUFFEREXT) \ -GLE(CheckFramebufferStatus, "CheckFramebufferStatusEXT", CHECKFRAMEBUFFERSTATUSEXT) \ -GLE(FramebufferRenderbuffer, "FramebufferRenderbufferEXT", FRAMEBUFFERRENDERBUFFEREXT) \ -GLE(FramebufferTexture2D, "FramebufferTexture2DEXT", FRAMEBUFFERTEXTURE2DEXT) \ -GLE(GenerateMipmap, "GenerateMipmapEXT", GENERATEMIPMAPEXT) \ -/* GL_EXT_framebuffer_blit */ \ -GLE(BlitFramebuffer, "BlitFramebufferEXT", BLITFRAMEBUFFEREXT) \ -/* GL_EXT_framebuffer_multisample */ \ -GLE(RenderbufferStorageMultisample, "RenderbufferStorageMultisampleEXT",RENDERBUFFERSTORAGEMULTISAMPLEEXT) \ -/* */ - -#define gdraw_GLx_(id) gdraw_GL_##id -#define GDRAW_GLx_(id) GDRAW_GL_##id -#define GDRAW_SHADERS "gdraw_gl_shaders.inl" - -// On Linux, GLhandleARB is void* but shader functions use GLuint values. -// Use GLuint as our handle type, matching the Mac pattern from gdraw_gl_shared.inl. -#define GDrawGLProgram GLuint -typedef GLuint GLhandle; -typedef gdraw_gl_resourcetype gdraw_resourcetype; - -// Declare extension function pointers -#define GLE(id, import, procname) static PFNGL##procname##PROC gl##id; -GDRAW_GL_EXTENSION_LIST -#undef GLE - -static void load_extensions(void) -{ - #define GLE(id, import, procname) \ - gl##id = (PFNGL##procname##PROC) SDL_GL_GetProcAddress("gl" import); - GDRAW_GL_EXTENSION_LIST - #undef GLE -} - -static void clear_renderstate_platform_specific(void) -{ - glDisable(GL_ALPHA_TEST); -} - -static void error_msg_platform_specific(const char *msg) -{ - fprintf(stderr, "[GDraw SDL] %s\n", msg); -} - -#define GDRAW_MULTISAMPLING - -// Suppress SIGTRAP from GL debug assertions on Linux -#ifdef RR_BREAK -#undef RR_BREAK -#endif -#define RR_BREAK() \ - do { fprintf(stderr, "[GDraw] RR_BREAK suppressed (GL error)\n"); } while(0) - -#include "../../../Windows64/Iggy/gdraw/gdraw_gl_shared.inl" - -// Context creation and management - -GDrawFunctions *gdraw_GL_CreateContext(S32 w, S32 h, S32 msaa_samples) -{ - static const TextureFormatDesc tex_formats[] = { - { IFT_FORMAT_rgba_8888, 1, 1, 4, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_rgba_4444_LE, 1, 1, 2, GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4 }, - { IFT_FORMAT_rgba_5551_LE, 1, 1, 2, GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1 }, - { IFT_FORMAT_la_88, 1, 1, 2, GL_LUMINANCE8_ALPHA8, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_la_44, 1, 1, 1, GL_LUMINANCE4_ALPHA4, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_i_8, 1, 1, 1, GL_INTENSITY8, GL_ALPHA, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_i_4, 1, 1, 1, GL_INTENSITY4, GL_ALPHA, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_l_8, 1, 1, 1, GL_LUMINANCE8, GL_LUMINANCE, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_l_4, 1, 1, 1, GL_LUMINANCE4, GL_LUMINANCE, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_DXT1, 4, 4, 8, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_DXT3, 4, 4, 16, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0, GL_UNSIGNED_BYTE }, - { IFT_FORMAT_DXT5, 4, 4, 16, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0, GL_UNSIGNED_BYTE }, - { 0, 0, 0, 0, 0, 0, 0 }, - }; - - GDrawFunctions *funcs; - const char *s; - GLint n; - - // A current SDL2 GL context must be active before calling this, if it doesn't exist, just throw an warning- - s = (const char *) glGetString(GL_EXTENSIONS); - if (!s) { - fprintf(stderr, "[GDraw SDL] glGetString(GL_EXTENSIONS) returned NULL - " - "SDL GL context not current?\n"); - assert(s != NULL); - return NULL; - } - - // Verify required extensions - if (!hasext(s, "GL_ARB_multitexture") || - !hasext(s, "GL_ARB_texture_compression") || - !hasext(s, "GL_ARB_texture_mirrored_repeat") || - !hasext(s, "GL_ARB_texture_non_power_of_two") || - !hasext(s, "GL_ARB_vertex_buffer_object") || - !hasext(s, "GL_EXT_framebuffer_object") || - !hasext(s, "GL_ARB_shader_objects") || - !hasext(s, "GL_ARB_vertex_shader") || - !hasext(s, "GL_ARB_fragment_shader")) - { - fprintf(stderr, "[GDraw SDL] Required GL extensions not available\n"); - return NULL; - } - - if (!hasext(s, "GL_EXT_framebuffer_multisample") && msaa_samples > 1) - return NULL; - - load_extensions(); - funcs = create_context(w, h); - if (!funcs) - return NULL; - - gdraw->tex_formats = tex_formats; - - gdraw->has_mapbuffer = true; // core in ARB_vertex_buffer_object - gdraw->has_depth24 = true; - gdraw->has_texture_max_level = true; // core GL - - if (hasext(s, "GL_EXT_packed_depth_stencil")) - gdraw->has_packed_depth_stencil = true; - - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &n); - gdraw->has_conditional_non_power_of_two = (n < 8192); - - if (msaa_samples > 1) { - glGetIntegerv(GL_MAX_SAMPLES, &n); - gdraw->multisampling = RR_MIN(msaa_samples, n); - } - - opengl_check(); - - fprintf(stderr, "[GDraw SDL] Context created successfully (%dx%d, msaa=%d)\n", - w, h, msaa_samples); - return funcs; -} - -void gdraw_GL_BeginCustomDraw_4J(IggyCustomDrawCallbackRegion *region, F32 *matrix) -{ - clear_renderstate(); - gdraw_GetObjectSpaceMatrix(matrix, region->o2w, gdraw->projection, depth_from_id(0), 0); -} - -void gdraw_GL_CalculateCustomDraw_4J(IggyCustomDrawCallbackRegion *region, F32 *matrix) -{ - gdraw_GetObjectSpaceMatrix(matrix, region->o2w, gdraw->projection, 0.0f, 0); -} diff --git a/Minecraft.Client/Platform/Linux/LinuxGL.cpp b/Minecraft.Client/Platform/Linux/LinuxGL.cpp index 351bb5d24..c7fff72d5 100644 --- a/Minecraft.Client/Platform/Linux/LinuxGL.cpp +++ b/Minecraft.Client/Platform/Linux/LinuxGL.cpp @@ -1,135 +1,113 @@ #ifdef __linux__ #include "../stdafx.h" - -#include -#include -#include -#include +#include "4J_Render.h" #include "../../Minecraft.World/IO/Streams/IntBuffer.h" #include "../../Minecraft.World/IO/Streams/FloatBuffer.h" #include "../../Minecraft.World/IO/Streams/ByteBuffer.h" -void LinuxGLLogLightmapState(const char* stage, int textureId, bool scaleLight) { - static int logCount = 0; - if (logCount >= 16) return; +extern C4JRender RenderManager; - ++logCount; +#ifdef GLES +extern "C" { +extern void glClearDepthf(float depth); +void glClearDepth(double depth) { glClearDepthf((float)depth); } +void glTexGeni(unsigned int, unsigned int, int) {} +void glTexGenfv(unsigned int, unsigned int, const float*) {} +void glTexCoordPointer(int, unsigned int, int, const void*) {} +void glNormalPointer(unsigned int, int, const void*) {} +void glColorPointer(int, unsigned int, int, const void*) {} +void glVertexPointer(int, unsigned int, int, const void*) {} +void glEndList(void) {} +void glCallLists(int, unsigned int, const void*) {} +} +#endif - static bool loggedSymbols = false; - if (!loggedSymbols) { - loggedSymbols = true; - app.DebugPrintf( - "[linux-lightmap] linuxgl symbols glActiveTexture=%p " - "glClientActiveTexture=%p glMultiTexCoord2f=%p\n", - reinterpret_cast(::glActiveTexture), - reinterpret_cast(::glClientActiveTexture), - reinterpret_cast(::glMultiTexCoord2f)); +inline int* getIntPtr(IntBuffer* buf) { + return buf ? (int*)buf->getBuffer() + buf->position() : nullptr; +} +inline void* getBytePtr(ByteBuffer* buf) { + return buf ? (char*)buf->getBuffer() + buf->position() : nullptr; +} + +void glGenTextures_4J(IntBuffer* buf) { + if (!buf) return; + int n = buf->limit() - buf->position(); + int* dst = getIntPtr(buf); + for (int i = 0; i < n; i++) dst[i] = RenderManager.TextureCreate(); +} + +void glDeleteTextures_4J(IntBuffer* buf) { + if (!buf) return; + int n = buf->limit() - buf->position(); + int* src = getIntPtr(buf); + for (int i = 0; i < n; i++) RenderManager.TextureFree(src[i]); +} + +void glTexImage2D_4J(int target, int level, int internalformat, int width, + int height, int border, int format, int type, + ByteBuffer* pixels) { + (void)target; + (void)internalformat; + (void)border; + (void)format; + (void)type; + RenderManager.TextureData(width, height, getBytePtr(pixels), level, + C4JRender::TEXTURE_FORMAT_RxGyBzAw); +} + +void glLight_4J(int light, int pname, FloatBuffer* params) { + const float* p = params->_getDataPointer(); + int idx = (light == 0x4001) ? 1 : 0; + if (pname == 0x1203) + RenderManager.StateSetLightDirection(idx, p[0], p[1], p[2]); + else if (pname == 0x1201) + RenderManager.StateSetLightColour(idx, p[0], p[1], p[2]); + else if (pname == 0x1200) + RenderManager.StateSetLightAmbientColour(p[0], p[1], p[2]); +} + +void glLightModel_4J(int pname, FloatBuffer* params) { + if (pname == 0x0B53) { + const float* p = params->_getDataPointer(); + RenderManager.StateSetLightAmbientColour(p[0], p[1], p[2]); } - - GLint activeTexture = 0; - GLint matrixMode = 0; - ::glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture); - ::glGetIntegerv(GL_MATRIX_MODE, &matrixMode); - - const GLint restoreTexture = activeTexture; - ::glActiveTexture(GL_TEXTURE1); - - GLint unit1Binding = 0; - ::glGetIntegerv(GL_TEXTURE_BINDING_2D, &unit1Binding); - const bool unit1Enabled = (::glIsEnabled(GL_TEXTURE_2D) == GL_TRUE); - - GLfloat textureMatrix[16]; - ::glGetFloatv(GL_TEXTURE_MATRIX, textureMatrix); - - ::glActiveTexture(restoreTexture); - - app.DebugPrintf( - "[linux-lightmap] %s tex=%d scale=%d active=%#x matrixMode=%#x " - "unit1Bound=%d unit1Enabled=%d texMatrix=[%.4f %.4f %.4f %.4f]\n", - stage, textureId, scaleLight ? 1 : 0, activeTexture, matrixMode, - unit1Binding, unit1Enabled ? 1 : 0, textureMatrix[0], textureMatrix[5], - textureMatrix[12], textureMatrix[13]); } -int glGenTextures() { - GLuint id = 0; - ::glGenTextures(1, &id); - return (int)id; +void glFog_4J(int pname, FloatBuffer* params) { + const float* p = params->_getDataPointer(); + if (pname == 0x0B66) RenderManager.StateSetFogColour(p[0], p[1], p[2]); } -void glGenTextures(IntBuffer* buf) { - GLuint id = 0; - ::glGenTextures(1, &id); - buf->put((int)id); - buf->flip(); +void glGetFloat_4J(int pname, FloatBuffer* params) { + const float* m = RenderManager.MatrixGet(pname); + if (m) memcpy(params->_getDataPointer(), m, 16 * sizeof(float)); } -void glDeleteTextures(int id) { - GLuint uid = (GLuint)id; - ::glDeleteTextures(1, &uid); -} - -void glDeleteTextures(IntBuffer* buf) { - int id = buf->get(0); - GLuint uid = (GLuint)id; - ::glDeleteTextures(1, &uid); -} - -void glLight(int light, int pname, FloatBuffer* params) { - ::glLightfv((GLenum)light, (GLenum)pname, params->_getDataPointer()); -} - -void glLightModel(int pname, FloatBuffer* params) { - ::glLightModelfv((GLenum)pname, params->_getDataPointer()); -} - -void glGetFloat(int pname, FloatBuffer* params) { - ::glGetFloatv((GLenum)pname, params->_getDataPointer()); -} - -void glTexGen(int coord, int pname, FloatBuffer* params) { - ::glTexGenfv((GLenum)coord, (GLenum)pname, params->_getDataPointer()); -} - -void glFog(int pname, FloatBuffer* params) { - ::glFogfv((GLenum)pname, params->_getDataPointer()); -} - -void glTexCoordPointer(int size, int type, FloatBuffer* pointer) { - ::glTexCoordPointer(size, (GLenum)type, 0, pointer->_getDataPointer()); -} - -void glNormalPointer(int type, ByteBuffer* pointer) { - ::glNormalPointer((GLenum)type, 0, pointer->getBuffer()); -} - -void glColorPointer(int size, bool normalized, int stride, - ByteBuffer* pointer) { - (void)normalized; - ::glColorPointer(size, GL_UNSIGNED_BYTE, stride, pointer->getBuffer()); -} - -void glVertexPointer(int size, int type, FloatBuffer* pointer) { - ::glVertexPointer(size, (GLenum)type, 0, pointer->_getDataPointer()); -} - -void glEndList(int) { ::glEndList(); } - -void glTexImage2D(int target, int level, int internalformat, int width, - int height, int border, int format, int type, - ByteBuffer* pixels) { - void* data = pixels ? pixels->getBuffer() : nullptr; - ::glTexImage2D((GLenum)target, level, internalformat, width, height, border, - (GLenum)format, (GLenum)type, data); -} - -void glCallLists(IntBuffer* lists) { +void glCallLists_4J(IntBuffer* lists) { + if (!lists) return; int count = lists->limit() - lists->position(); - ::glCallLists(count, GL_INT, lists->getBuffer()); + int* ids = getIntPtr(lists); + for (int i = 0; i < count; i++) RenderManager.CBuffCall(ids[i], false); } +void glReadPixels_4J(int x, int y, int w, int h, int f, int t, ByteBuffer* p) { + (void)f; + (void)t; + RenderManager.ReadPixels(x, y, w, h, getBytePtr(p)); +} + +// dead stubs +void glTexCoordPointer_4J(int, int, FloatBuffer*) {} +void glNormalPointer_4J(int, ByteBuffer*) {} +void glColorPointer_4J(int, bool, int, ByteBuffer*) {} +void glVertexPointer_4J(int, int, FloatBuffer*) {} +void glEndList_4J(int) {} +void glTexGen_4J(int, int, FloatBuffer*) {} + +// query objects +#include static PFNGLGENQUERIESARBPROC _glGenQueriesARB = nullptr; static PFNGLBEGINQUERYARBPROC _glBeginQueryARB = nullptr; static PFNGLENDQUERYARBPROC _glEndQueryARB = nullptr; @@ -148,40 +126,41 @@ static void initQueryFuncs() { RTLD_DEFAULT, "glGetQueryObjectuivARB"); } -void glGenQueriesARB(IntBuffer* buf) { +void glGenQueriesARB_4J(IntBuffer* buf) { initQueryFuncs(); - if (_glGenQueriesARB) { - GLuint id = 0; - _glGenQueriesARB(1, &id); - buf->put((int)id); - buf->flip(); + if (_glGenQueriesARB && buf) { + int n = buf->limit() - buf->position(); + if (n > 0) _glGenQueriesARB(n, (GLuint*)getIntPtr(buf)); } } -void glBeginQueryARB(int target, int id) { +void glBeginQueryARB_4J(int target, int id) { initQueryFuncs(); if (_glBeginQueryARB) _glBeginQueryARB((GLenum)target, (GLuint)id); } -void glEndQueryARB(int target) { +void glEndQueryARB_4J(int target) { initQueryFuncs(); if (_glEndQueryARB) _glEndQueryARB((GLenum)target); } -void glGetQueryObjectuARB(int id, int pname, IntBuffer* params) { +void glGetQueryObjectuARB_4J(int id, int pname, IntBuffer* params) { initQueryFuncs(); - if (_glGetQueryObjectuivARB) { - GLuint val = 0; - _glGetQueryObjectuivARB((GLuint)id, (GLenum)pname, &val); - params->put((int)val); - params->flip(); - } + if (_glGetQueryObjectuivARB && params) + // LWJGL does not change limits/positions during these calls, it + // reads/writes exactly at pointer!! + _glGetQueryObjectuivARB((GLuint)id, (GLenum)pname, + (GLuint*)getIntPtr(params)); } - -void glReadPixels(int x, int y, int width, int height, int format, int type, - ByteBuffer* pixels) { - ::glReadPixels(x, y, width, height, (GLenum)format, (GLenum)type, - pixels->getBuffer()); +void glGetFloat(int pname, FloatBuffer* params) { + glGetFloat_4J(pname, params); } - -#endif +void LinuxGLLogLightmapState(const char* stage, int textureId, + bool scaleLight) { + static int logCount = 0; + if (logCount >= 16) return; + ++logCount; + fprintf(stderr, "[linux-lightmap] %s tex=%d scale=%d\n", stage, textureId, + scaleLight ? 1 : 0); +} +#endif \ No newline at end of file diff --git a/Minecraft.Client/Platform/Linux/Linux_UIController.cpp b/Minecraft.Client/Platform/Linux/Linux_UIController.cpp index 4a39e85ef..855c10b9d 100644 --- a/Minecraft.Client/Platform/Linux/Linux_UIController.cpp +++ b/Minecraft.Client/Platform/Linux/Linux_UIController.cpp @@ -1,12 +1,8 @@ #include "../../../Minecraft.World/Platform/stdafx.h" #include "Linux_UIController.h" -// Temp -#include "../../Minecraft.h" -#include "../../Textures/Textures.h" - // GDraw GL backend for Linux -#include "Iggy/gdraw/gdraw_sdl.h" +#include "Iggy/gdraw/gdraw.h" ConsoleUIController ui; @@ -22,14 +18,12 @@ static void restoreFixedFunctionStateAfterIggy() { glClientActiveTexture(GL_TEXTURE1); glActiveTexture(GL_TEXTURE1); glDisable(GL_TEXTURE_2D); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glMatrixMode(GL_TEXTURE); glLoadIdentity(); glClientActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0); glEnable(GL_TEXTURE_2D); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glMatrixMode(GL_TEXTURE); glLoadIdentity(); @@ -54,7 +48,7 @@ void ConsoleUIController::init(S32 w, S32 h) { gdraw_GL_SetResourceLimits(GDRAW_GL_RESOURCE_texture, 5000, 128 * 1024 * 1024); gdraw_GL_SetResourceLimits(GDRAW_GL_RESOURCE_rendertarget, 10, - 32 * 1024 * 1024); + 64 * 1024 * 1024); IggySetGDraw(gdraw_funcs); #endif diff --git a/Minecraft.Client/Platform/Linux/Stubs/iggy_stubs.h b/Minecraft.Client/Platform/Linux/Stubs/iggy_stubs.h index 701ad994f..ca0c47ff1 100644 --- a/Minecraft.Client/Platform/Linux/Stubs/iggy_stubs.h +++ b/Minecraft.Client/Platform/Linux/Stubs/iggy_stubs.h @@ -254,6 +254,14 @@ RADEXPFUNC inline void* RADEXPLINK IggyPerfmonCreate( } RADEXPFUNC inline void RADEXPLINK IggyInstallPerfmon(void* perfmon) { STUBBED; } +RADEXPFUNC inline IggyResult RADEXPLINK IggyValueGetTypeRS(IggyValuePath* var, + IggyName sub_name, + char const* sub_name_utf8, + IggyDatatype* result) { + STUBBED; + return IGGY_RESULT_SUCCESS; +} + // GDraw memory/warning functions are defined in gdraw_glfw.c (C linkage) // Juicey you stupid idiot do NOT define them here diff --git a/Minecraft.Client/Platform/Windows64/Iggy/gdraw/gdraw_gl_shared.inl b/Minecraft.Client/Platform/Windows64/Iggy/gdraw/gdraw_gl_shared.inl index c22517041..e24dd7202 100644 --- a/Minecraft.Client/Platform/Windows64/Iggy/gdraw/gdraw_gl_shared.inl +++ b/Minecraft.Client/Platform/Windows64/Iggy/gdraw/gdraw_gl_shared.inl @@ -57,6 +57,10 @@ static RADINLINE void break_on_err(GLint e) #endif } +#ifndef GDRAW_PLATFORM_REPORT_GL_SITE +#define GDRAW_PLATFORM_REPORT_GL_SITE(site) ((void)0) +#endif + static void report_err(GLint e) { break_on_err(e); @@ -74,17 +78,31 @@ static void eat_gl_err(void) while (glGetError() != GL_NO_ERROR); } +static void opengl_check_site(const char *site); + static void opengl_check(void) +{ + opengl_check_site(NULL); +} + +static void opengl_check_site(const char *site) { #ifdef _DEBUG GLint e = glGetError(); if (e != GL_NO_ERROR) { + GDRAW_PLATFORM_REPORT_GL_SITE(site); report_err(e); eat_gl_err(); } +#else + (void) site; #endif } +#ifndef OPENGL_CHECK_SITE +#define OPENGL_CHECK_SITE(site) opengl_check_site(site) +#endif + static U32 is_pow2(S32 n) { return ((U32) n & (U32) (n-1)) == 0; @@ -635,6 +653,90 @@ static void RADLINK gdraw_DescribeVertexBuffer(GDrawVertexBuffer *vbuf, GDraw_Ve // Create/free (or cache) render targets // +#ifdef __linux__ +typedef struct +{ + S32 free_count; + S32 live_count; + S32 locked_count; + S32 dead_count; + S32 pinned_count; + S32 user_owned_count; + S32 alloc_count; +} GDrawHandleStateCounts; + +enum +{ + GDRAW_RT_DIAG_color_memory = 1 << 0, + GDRAW_RT_DIAG_color_handles = 1 << 1, + GDRAW_RT_DIAG_cache_bitmap = 1 << 2, + GDRAW_RT_DIAG_stack_depth = 1 << 3, + GDRAW_RT_DIAG_empty_request = 1 << 4, +}; + +static U32 gdraw_rt_diag_emitted = 0; + +static void gdraw_CountHandleStates(GDrawHandleCache *cache, + GDrawHandleStateCounts *counts) +{ + S32 i; + counts->free_count = 0; + counts->live_count = 0; + counts->locked_count = 0; + counts->dead_count = 0; + counts->pinned_count = 0; + counts->user_owned_count = 0; + counts->alloc_count = 0; + + if (!cache) + return; + + for (i = 0; i < cache->max_handles; ++i) { + switch (cache->handle[i].state) { + case GDRAW_HANDLE_STATE_free: ++counts->free_count; break; + case GDRAW_HANDLE_STATE_live: ++counts->live_count; break; + case GDRAW_HANDLE_STATE_locked: ++counts->locked_count; break; + case GDRAW_HANDLE_STATE_dead: ++counts->dead_count; break; + case GDRAW_HANDLE_STATE_pinned: ++counts->pinned_count; break; + case GDRAW_HANDLE_STATE_user_owned: ++counts->user_owned_count; break; + case GDRAW_HANDLE_STATE_alloc: ++counts->alloc_count; break; + default: break; + } + } +} + +static void gdraw_ReportHandleCacheDiag(char const *label, + GDrawHandleCache *cache, + U32 once_bit, + S32 req_w, + S32 req_h) +{ + GDrawHandleStateCounts counts; + + if (gdraw_rt_diag_emitted & once_bit) + return; + gdraw_rt_diag_emitted |= once_bit; + + gdraw_CountHandleStates(cache, &counts); + IggyGDrawSendWarning(NULL, + "GDraw[%s] diag: frame=%dx%d tile=%dx%d padded=%dx%d req=%dx%d depth=%d bytes_free=%d total_bytes=%d handles free=%d live=%d locked=%d dead=%d pinned=%d user=%d alloc=%d", + label, + gdraw->frametex_width, gdraw->frametex_height, + gdraw->tw, gdraw->th, + gdraw->tpw, gdraw->tph, + req_w, req_h, + (int) (gdraw->cur - gdraw->frame), + cache ? cache->bytes_free : 0, + cache ? cache->total_bytes : 0, + counts.free_count, counts.live_count, counts.locked_count, + counts.dead_count, counts.pinned_count, counts.user_owned_count, + counts.alloc_count); +} +#else +#define gdraw_ReportHandleCacheDiag(label, cache, once_bit, req_w, req_h) \ + ((void) 0) +#endif + static GDrawHandle *get_rendertarget_texture(int width, int height, void *owner, GDrawStats *gstats) { S32 size; @@ -678,13 +780,17 @@ static GDrawHandle *get_color_rendertarget(GDrawStats *gstats) // ran out of RTs, allocate a new one size = gdraw->frametex_width * gdraw->frametex_height * 4; if (gdraw->rendertargets.bytes_free < size) { - IggyGDrawSendWarning(NULL, "GDraw exceeded available rendertarget memory"); + gdraw_ReportHandleCacheDiag("rt-color-memory", &gdraw->rendertargets, + GDRAW_RT_DIAG_color_memory, gdraw->tpw, gdraw->tph); + IggyGDrawSendWarning(NULL, "GDraw[rt-color] exceeded available rendertarget memory"); return NULL; } t = gdraw_HandleCacheAllocateBegin(&gdraw->rendertargets); if (!t) { - IggyGDrawSendWarning(NULL, "GDraw exceeded available rendertarget handles"); + gdraw_ReportHandleCacheDiag("rt-color-handles", &gdraw->rendertargets, + GDRAW_RT_DIAG_color_handles, gdraw->tpw, gdraw->tph); + IggyGDrawSendWarning(NULL, "GDraw[rt-color] exceeded available rendertarget handles"); return t; } @@ -992,25 +1098,31 @@ static rrbool RADLINK gdraw_TextureDrawBufferBegin(gswf_recti *region, gdraw_tex GDrawHandle *t; int k; if (gdraw->tw == 0 || gdraw->th == 0) { - IggyGDrawSendWarning(NULL, "GDraw got a request for an empty rendertarget"); + gdraw_ReportHandleCacheDiag("rt-empty", &gdraw->rendertargets, + GDRAW_RT_DIAG_empty_request, region->x1 - region->x0, region->y1 - region->y0); + IggyGDrawSendWarning(NULL, "GDraw[rt-stack] got a request for an empty rendertarget"); return false; } if (n >= &gdraw->frame[MAX_RENDER_STACK_DEPTH]) { - IggyGDrawSendWarning(NULL, "GDraw rendertarget nesting exceeded MAX_RENDER_STACK_DEPTH"); + gdraw_ReportHandleCacheDiag("rt-stack-depth", &gdraw->rendertargets, + GDRAW_RT_DIAG_stack_depth, region->x1 - region->x0, region->y1 - region->y0); + IggyGDrawSendWarning(NULL, "GDraw[rt-stack] rendertarget nesting exceeded MAX_RENDER_STACK_DEPTH"); return false; } if (owner) { t = get_rendertarget_texture(region->x1 - region->x0, region->y1 - region->y0, owner, gstats); if (!t) { - IggyGDrawSendWarning(NULL, "GDraw ran out of rendertargets for cacheAsBItmap"); + gdraw_ReportHandleCacheDiag("rt-cacheAsBitmap", gdraw->texturecache, + GDRAW_RT_DIAG_cache_bitmap, region->x1 - region->x0, region->y1 - region->y0); + IggyGDrawSendWarning(NULL, "GDraw[rt-cacheAsBitmap] ran out of rendertargets"); return false; } } else { t = get_color_rendertarget(gstats); if (!t) { - IggyGDrawSendWarning(NULL, "GDraw ran out of rendertargets"); + IggyGDrawSendWarning(NULL, "GDraw[rt-color] ran out of rendertargets"); return false; } } @@ -1355,18 +1467,18 @@ static int set_render_state(GDrawRenderState *r, S32 vformat, const int **ovvars } use_lazy_shader(prg); - opengl_check(); + OPENGL_CHECK_SITE("set_render_state:use_lazy_shader"); fvars = prg->vars[0]; vvars = prg->vars[1]; if (vformat == GDRAW_vformat_ihud1) { F32 wv[2][4] = { 1.0f/960,0,0,-1.0, 0,-1.0f/540,0,+1.0 }; glUniform4fv(vvars[VAR_ihudv_worldview], 2, wv[0]); - opengl_check(); + OPENGL_CHECK_SITE("set_render_state:ihud_worldview"); glUniform4fv(vvars[VAR_ihudv_material], p->uniform_count, p->uniforms); - opengl_check(); + OPENGL_CHECK_SITE("set_render_state:ihud_material"); glUniform1f(vvars[VAR_ihudv_textmode], p->drawprim_mode ? 0.0f : 1.0f); - opengl_check(); + OPENGL_CHECK_SITE("set_render_state:ihud_textmode"); } else { // set vertex shader constants @@ -1393,7 +1505,7 @@ static int set_render_state(GDrawRenderState *r, S32 vformat, const int **ovvars // texture stuff set_texture(0, r->tex[0]); - opengl_check(); + OPENGL_CHECK_SITE("set_render_state:set_texture0"); if (r->tex[0] && gdraw->has_conditional_non_power_of_two && ((GDrawHandle*) r->tex[0])->handle.tex.nonpow2) { // only wrap mode allowed in conditional nonpow2 is clamp; this should @@ -1490,7 +1602,7 @@ static int set_render_state(GDrawRenderState *r, S32 vformat, const int **ovvars else glDepthMask(GL_FALSE); - opengl_check(); + OPENGL_CHECK_SITE("set_render_state:final"); if (ovvars) *ovvars = vvars; @@ -1611,7 +1723,7 @@ static void RADLINK gdraw_DrawIndexedTriangles(GDrawRenderState *r, GDrawPrimiti glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } - opengl_check(); + OPENGL_CHECK_SITE("gdraw_DrawIndexedTriangles:final"); tag_resources(vb, r->tex[0], r->tex[1]); } @@ -1628,33 +1740,33 @@ static void do_screen_quad(gswf_recti *s, F32 *tc, const int *vvars, GDrawStats F32 vert[4][4]; F32 world[2*4]; - opengl_check(); + OPENGL_CHECK_SITE("do_screen_quad:begin"); vert[0][0] = px0; vert[0][1] = py0; vert[0][2] = s0; vert[0][3] = t0; vert[1][0] = px1; vert[1][1] = py0; vert[1][2] = s1; vert[1][3] = t0; vert[2][0] = px1; vert[2][1] = py1; vert[2][2] = s1; vert[2][3] = t1; vert[3][0] = px0; vert[3][1] = py1; vert[3][2] = s0; vert[3][3] = t1; - opengl_check(); + OPENGL_CHECK_SITE("do_screen_quad:after_vertices"); gdraw_PixelSpace(world); world[2] = depth; set_world_projection(vvars, world); - opengl_check(); + OPENGL_CHECK_SITE("do_screen_quad:after_projection"); set_vertex_format(GDRAW_vformat_v2tc2, vert[0]); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - opengl_check(); + OPENGL_CHECK_SITE("do_screen_quad:before_draw"); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); reset_vertex_format(GDRAW_vformat_v2tc2); - opengl_check(); + OPENGL_CHECK_SITE("do_screen_quad:after_draw"); gstats->nonzero_flags |= GDRAW_STATS_batches; gstats->num_batches += 1; gstats->drawn_vertices += 4; gstats->drawn_indices += 6; - opengl_check(); + OPENGL_CHECK_SITE("do_screen_quad:final"); } #ifdef GDRAW_FEWER_CLEARS @@ -1805,7 +1917,7 @@ static void RADLINK gdraw_FilterQuad(GDrawRenderState *r, S32 x0, S32 y0, S32 x1 glColorMask(1,1,1,1); glDisable(GL_BLEND); glDisable(GL_DEPTH_TEST); - opengl_check(); + OPENGL_CHECK_SITE("gdraw_FilterQuad:pre_filter"); if (r->blend_mode == GDRAW_BLEND_filter) { switch (r->filter) { diff --git a/Minecraft.Client/Platform/stdafx.h b/Minecraft.Client/Platform/stdafx.h index aa113c2c0..e305669a7 100644 --- a/Minecraft.Client/Platform/stdafx.h +++ b/Minecraft.Client/Platform/stdafx.h @@ -5,8 +5,8 @@ #pragma once -//#include -//#include +// #include +// #include #define __STR2__(x) #x #define __STR1__(x) __STR2__(x) @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include #include #include @@ -49,7 +49,7 @@ #include #include #include -#include +#include #include #include "../Platform/PSVita/PSVitaExtras/PSVitaTypes.h" #include "../Platform/PSVita/PSVitaExtras/PSVitaStubs.h" @@ -71,8 +71,8 @@ typedef unsigned __int64 __uint64; #endif -#ifdef _WINDOWS64 -#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#ifdef _WINDOWS64 +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers // Windows Header Files: #include #include @@ -80,14 +80,12 @@ typedef unsigned __int64 __uint64; // TODO: reference additional headers your program requires here #include #include -using namespace DirectX; +using namespace DirectX; #define HRESULT_SUCCEEDED(hr) (((HRESULT)(hr)) >= 0) #endif - - #ifdef _DURANGO #include #include @@ -95,14 +93,12 @@ using namespace DirectX; #include #include #include -using namespace DirectX; +using namespace DirectX; #include #include "../Platform/Durango/DurangoExtras/DurangoStubs.h" #define HRESULT_SUCCEEDED(hr) (((HRESULT)(hr)) >= 0) #endif - - #ifdef _XBOX #include #include @@ -166,42 +162,42 @@ typedef XUID GameSessionUID; #include "../../Minecraft.World/Util/PerformanceTimer.h" #ifdef _XBOX - #include "xbox/4JLibs/inc/4J_Input.h" - #include "xbox/4JLibs/inc/4J_Profile.h" - #include "xbox/4JLibs/inc/4J_Render.h" - #include "xbox/4JLibs/inc/4J_XTMS.h" - #include "xbox/4JLibs/inc/4J_Storage.h" -#elif defined (__PS3__) +#include "xbox/4JLibs/inc/4J_Input.h" +#include "xbox/4JLibs/inc/4J_Profile.h" +#include "xbox/4JLibs/inc/4J_Render.h" +#include "xbox/4JLibs/inc/4J_XTMS.h" +#include "xbox/4JLibs/inc/4J_Storage.h" +#elif defined(__PS3__) - #include "../Platform/PS3/4JLibs/inc/4J_Input.h" - #include "../Platform/PS3/4JLibs/inc/4J_Profile.h" - #include "../Platform/PS3/4JLibs/inc/4J_Render.h" - #include "../Platform/PS3/4JLibs/inc/4J_Storage.h" +#include "../Platform/PS3/4JLibs/inc/4J_Input.h" +#include "../Platform/PS3/4JLibs/inc/4J_Profile.h" +#include "../Platform/PS3/4JLibs/inc/4J_Render.h" +#include "../Platform/PS3/4JLibs/inc/4J_Storage.h" #elif defined _DURANGO - #include "../Platform/Durango/4JLibs/inc/4J_Input.h" - #include "../Platform/Durango/4JLibs/inc/4J_Profile.h" - #include "../Platform/Durango/4JLibs/inc/4J_Render.h" - #include "../Platform/Durango/4JLibs/inc/4J_Storage.h" +#include "../Platform/Durango/4JLibs/inc/4J_Input.h" +#include "../Platform/Durango/4JLibs/inc/4J_Profile.h" +#include "../Platform/Durango/4JLibs/inc/4J_Render.h" +#include "../Platform/Durango/4JLibs/inc/4J_Storage.h" #elif defined _WINDOWS64 - #include "../Platform/Windows64/4JLibs/inc/4J_Input.h" - #include "../Platform/Windows64/4JLibs/inc/4J_Profile.h" - #include "../Platform/Windows64/4JLibs/inc/4J_Render.h" - #include "../Platform/Windows64/4JLibs/inc/4J_Storage.h" +#include "../Platform/Windows64/4JLibs/inc/4J_Input.h" +#include "../Platform/Windows64/4JLibs/inc/4J_Profile.h" +#include "../Platform/Windows64/4JLibs/inc/4J_Render.h" +#include "../Platform/Windows64/4JLibs/inc/4J_Storage.h" #elif defined __linux__ - #include "4J_Input.h" - #include "4J_Profile.h" - #include "4J_Render.h" - #include "4J_Storage.h" +#include "4J_Input.h" +#include "4J_Profile.h" +#include "4J_Render.h" +#include "4J_Storage.h" #elif defined __PSVITA__ - #include "../Platform/PSVita/4JLibs/inc/4J_Input.h" - #include "../Platform/PSVita/4JLibs/inc/4J_Profile.h" - #include "../Platform/PSVita/4JLibs/inc/4J_Render.h" - #include "../Platform/PSVita/4JLibs/inc/4J_Storage.h" +#include "../Platform/PSVita/4JLibs/inc/4J_Input.h" +#include "../Platform/PSVita/4JLibs/inc/4J_Profile.h" +#include "../Platform/PSVita/4JLibs/inc/4J_Render.h" +#include "../Platform/PSVita/4JLibs/inc/4J_Storage.h" #else - #include "../Platform/Orbis/4JLibs/inc/4J_Input.h" - #include "../Platform/Orbis/4JLibs/inc/4J_Profile.h" - #include "../Platform/Orbis/4JLibs/inc/4J_Render.h" - #include "../Platform/Orbis/4JLibs/inc/4J_Storage.h" +#include "../Platform/Orbis/4JLibs/inc/4J_Input.h" +#include "../Platform/Orbis/4JLibs/inc/4J_Profile.h" +#include "../Platform/Orbis/4JLibs/inc/4J_Render.h" +#include "../Platform/Orbis/4JLibs/inc/4J_Storage.h" #endif #include "../Textures/Textures.h" @@ -229,7 +225,7 @@ typedef XUID GameSessionUID; #include "../Platform/Common/App_enums.h" #include "../Platform/Common/Tutorial/TutorialEnum.h" #include "../Platform/Common/App_structs.h" -//#endif +// #endif #include "../Platform/Common/Consoles_App.h" #include "../Platform/Common/Minecraft_Macros.h" @@ -242,95 +238,94 @@ typedef XUID GameSessionUID; #include "strings.h" #ifdef _XBOX - #include "../Platform/Xbox/Xbox_App.h" - #include "../Platform/Xbox/Sentient/MinecraftTelemetry.h" - #include "../Platform/Xbox/Sentient/DynamicConfigurations.h" - #include "../Platform/Xbox/Sentient/SentientTelemetryCommon.h" - #include "../Platform/Xbox/Sentient/Include/SenClientStats.h" - #include "../Platform/Xbox/GameConfig/Minecraft.spa.h" - #include "../../Minecraft.Assets/XboxMedia/4J_strings.h" - #include "../Platform/Xbox/XML/ATGXmlParser.h" - #include "../Platform/Xbox/Leaderboards/XboxLeaderboardManager.h" - #include "../Platform/Xbox/Social/SocialManager.h" - #include "../Platform/Xbox/Audio/SoundEngine.h" - #include "../Platform/Xbox/Xbox_UIController.h" +#include "../Platform/Xbox/Xbox_App.h" +#include "../Platform/Xbox/Sentient/MinecraftTelemetry.h" +#include "../Platform/Xbox/Sentient/DynamicConfigurations.h" +#include "../Platform/Xbox/Sentient/SentientTelemetryCommon.h" +#include "../Platform/Xbox/Sentient/Include/SenClientStats.h" +#include "../Platform/Xbox/GameConfig/Minecraft.spa.h" +#include "../../Minecraft.Assets/XboxMedia/4J_strings.h" +#include "../Platform/Xbox/XML/ATGXmlParser.h" +#include "../Platform/Xbox/Leaderboards/XboxLeaderboardManager.h" +#include "../Platform/Xbox/Social/SocialManager.h" +#include "../Platform/Xbox/Audio/SoundEngine.h" +#include "../Platform/Xbox/Xbox_UIController.h" -#elif defined (__PS3__) - #include "extraX64client.h" - #include "../Platform/PS3/Sentient/MinecraftTelemetry.h" - #include "../Platform/PS3/Sentient/DynamicConfigurations.h" - #include "../Platform/PS3/Sentient/SentientTelemetryCommon.h" - #include "../Platform/PS3/PS3_App.h" - #include "../Platform/PS3/GameConfig/Minecraft.spa.h" - #include "../../Minecraft.Assets/PS3Media/4J_strings.h" - #include "../Platform/PS3/XML/ATGXmlParser.h" - #include "../Platform/PS3/Social/SocialManager.h" - #include "../Platform/Common/Audio/SoundEngine.h" - #include "../Platform/PS3/Iggy/include/iggy.h" - #include "../Platform/PS3/Iggy/gdraw/gdraw_ps3gcm.h" - #include "../Platform/PS3/PS3_UIController.h" +#elif defined(__PS3__) +#include "extraX64client.h" +#include "../Platform/PS3/Sentient/MinecraftTelemetry.h" +#include "../Platform/PS3/Sentient/DynamicConfigurations.h" +#include "../Platform/PS3/Sentient/SentientTelemetryCommon.h" +#include "../Platform/PS3/PS3_App.h" +#include "../Platform/PS3/GameConfig/Minecraft.spa.h" +#include "../../Minecraft.Assets/PS3Media/4J_strings.h" +#include "../Platform/PS3/XML/ATGXmlParser.h" +#include "../Platform/PS3/Social/SocialManager.h" +#include "../Platform/Common/Audio/SoundEngine.h" +#include "../Platform/PS3/Iggy/include/iggy.h" +#include "../Platform/PS3/Iggy/gdraw/gdraw_ps3gcm.h" +#include "../Platform/PS3/PS3_UIController.h" #elif defined _DURANGO - #include "../Platform/Durango/Sentient/MinecraftTelemetry.h" - #include "../Platform/Durango/Durango_App.h" - #include "../Platform/Durango/Sentient/DynamicConfigurations.h" - #include "../Platform/Durango/Sentient/TelemetryEnum.h" - #include "../Platform/Durango/Sentient/SentientTelemetryCommon.h" - #include "../Platform/Durango/PresenceIds.h" - #include "../../Minecraft.Assets/DurangoMedia/4J_strings.h" - #include "../Platform/Durango/XML/ATGXmlParser.h" - #include "../Platform/Durango/Social/SocialManager.h" - #include "../Platform/Common/Audio/SoundEngine.h" - #include "../Platform/Durango/Iggy/include/iggy.h" - #include "../Platform/Durango/Iggy/gdraw/gdraw_d3d11.h" - #include "../Platform/Durango/Durango_UIController.h" +#include "../Platform/Durango/Sentient/MinecraftTelemetry.h" +#include "../Platform/Durango/Durango_App.h" +#include "../Platform/Durango/Sentient/DynamicConfigurations.h" +#include "../Platform/Durango/Sentient/TelemetryEnum.h" +#include "../Platform/Durango/Sentient/SentientTelemetryCommon.h" +#include "../Platform/Durango/PresenceIds.h" +#include "../../Minecraft.Assets/DurangoMedia/4J_strings.h" +#include "../Platform/Durango/XML/ATGXmlParser.h" +#include "../Platform/Durango/Social/SocialManager.h" +#include "../Platform/Common/Audio/SoundEngine.h" +#include "../Platform/Durango/Iggy/include/iggy.h" +#include "../Platform/Durango/Iggy/gdraw/gdraw_d3d11.h" +#include "../Platform/Durango/Durango_UIController.h" #elif defined _WINDOWS64 - #include "../Platform/Windows64/Sentient/MinecraftTelemetry.h" - #include "../Platform/Windows64/Windows64_App.h" - #include "../Platform/Windows64/Sentient/DynamicConfigurations.h" - #include "../Platform/Windows64/Sentient/SentientTelemetryCommon.h" - #include "../Platform/Windows64/GameConfig/Minecraft.spa.h" - #include "../Platform/Windows64/XML/ATGXmlParser.h" - #include "../Platform/Windows64/Social/SocialManager.h" - #include "../Platform/Common/Audio/SoundEngine.h" - #include "../Platform/Windows64/Iggy/include/iggy.h" - #include "../Platform/Windows64/Iggy/gdraw/gdraw_d3d11.h" - #include "../Platform/Windows64/Windows64_UIController.h" +#include "../Platform/Windows64/Sentient/MinecraftTelemetry.h" +#include "../Platform/Windows64/Windows64_App.h" +#include "../Platform/Windows64/Sentient/DynamicConfigurations.h" +#include "../Platform/Windows64/Sentient/SentientTelemetryCommon.h" +#include "../Platform/Windows64/GameConfig/Minecraft.spa.h" +#include "../Platform/Windows64/XML/ATGXmlParser.h" +#include "../Platform/Windows64/Social/SocialManager.h" +#include "../Platform/Common/Audio/SoundEngine.h" +#include "../Platform/Windows64/Iggy/include/iggy.h" +#include "../Platform/Windows64/Iggy/gdraw/gdraw_d3d11.h" +#include "../Platform/Windows64/Windows64_UIController.h" #elif defined __linux__ - // Linux build: avoid pulling in Windows64 platform headers (they cause - // symbol/class redefinitions). Use Orbis-compatible stubs and Linux controller. - #include "../Platform/Linux/Linux_App.h" - #include "../Platform/Linux/Iggy/include/iggy.h" - #include "../Platform/Linux/Sentient/SentientTelemetryCommon.h" - #include "../Platform/Linux/Sentient/DynamicConfigurations.h" - #include "../Platform/Orbis/GameConfig/Minecraft.spa.h" - #include "../Platform/Common/Audio/SoundEngine.h" - #include "../Platform/Linux/Linux_UIController.h" - #include "../Platform/Linux/Social/SocialManager.h" +// todo: cleanup ts +#include "../Platform/Linux/Linux_App.h" +#include "../Platform/Linux/Iggy/include/iggy.h" +#include "../Platform/Linux/Sentient/SentientTelemetryCommon.h" +#include "../Platform/Linux/Sentient/DynamicConfigurations.h" +#include "../Platform/Orbis/GameConfig/Minecraft.spa.h" +#include "../Platform/Common/Audio/SoundEngine.h" +#include "../Platform/Linux/Linux_UIController.h" +#include "../Platform/Linux/Social/SocialManager.h" #elif defined __PSVITA__ - #include "../Platform/PSVita/PSVita_App.h" - #include "../Platform/PSVita/Sentient/SentientManager.h" - #include "../Platform/PSVita/Sentient/MinecraftTelemetry.h" - #include "../Platform/PSVita/Sentient/DynamicConfigurations.h" - #include "../Platform/PSVita/GameConfig/Minecraft.spa.h" - #include "../Platform/PSVita/XML/ATGXmlParser.h" - #include "../Platform/PSVita/Social/SocialManager.h" - #include "../Platform/Common/Audio/SoundEngine.h" - #include "../Platform/PSVita/Iggy/include/iggy.h" - #include "../Platform/PSVita/Iggy/gdraw/gdraw_psp2.h" - #include "../Platform/PSVita/PSVita_UIController.h" +#include "../Platform/PSVita/PSVita_App.h" +#include "../Platform/PSVita/Sentient/SentientManager.h" +#include "../Platform/PSVita/Sentient/MinecraftTelemetry.h" +#include "../Platform/PSVita/Sentient/DynamicConfigurations.h" +#include "../Platform/PSVita/GameConfig/Minecraft.spa.h" +#include "../Platform/PSVita/XML/ATGXmlParser.h" +#include "../Platform/PSVita/Social/SocialManager.h" +#include "../Platform/Common/Audio/SoundEngine.h" +#include "../Platform/PSVita/Iggy/include/iggy.h" +#include "../Platform/PSVita/Iggy/gdraw/gdraw_psp2.h" +#include "../Platform/PSVita/PSVita_UIController.h" #else - #include "../Platform/Orbis/Sentient/MinecraftTelemetry.h" - #include "../Platform/Orbis/Orbis_App.h" - #include "../Platform/Orbis/Sentient/SentientTelemetryCommon.h" - #include "../Platform/Orbis/Sentient/DynamicConfigurations.h" - #include "../Platform/Orbis/GameConfig/Minecraft.spa.h" - #include "../../Minecraft.Assets/OrbisMedia/4J_strings.h" - #include "../Platform/Orbis/XML/ATGXmlParser.h" - #include "../Platform/Windows64/Social/SocialManager.h" - #include "../Platform/Common/Audio/SoundEngine.h" - #include "../Platform/Orbis/Iggy/include/iggy.h" - #include "../Platform/Orbis/Iggy/gdraw/gdraw_orbis.h" - #include "../Platform/Orbis/Orbis_UIController.h" +#include "../Platform/Orbis/Sentient/MinecraftTelemetry.h" +#include "../Platform/Orbis/Orbis_App.h" +#include "../Platform/Orbis/Sentient/SentientTelemetryCommon.h" +#include "../Platform/Orbis/Sentient/DynamicConfigurations.h" +#include "../Platform/Orbis/GameConfig/Minecraft.spa.h" +#include "../../Minecraft.Assets/OrbisMedia/4J_strings.h" +#include "../Platform/Orbis/XML/ATGXmlParser.h" +#include "../Platform/Windows64/Social/SocialManager.h" +#include "../Platform/Common/Audio/SoundEngine.h" +#include "../Platform/Orbis/Iggy/include/iggy.h" +#include "../Platform/Orbis/Iggy/gdraw/gdraw_orbis.h" +#include "../Platform/Orbis/Orbis_UIController.h" #endif #ifdef _XBOX @@ -358,13 +353,11 @@ typedef XUID GameSessionUID; #include "../Platform/Common/Telemetry/TelemetryManager.h" #ifdef _XBOX -//#include "../Platform/Xbox/Xbox_App.h" +// #include "../Platform/Xbox/Xbox_App.h" #elif !defined(__PS3__) #include "extraX64client.h" #endif - - #ifdef _FINAL_BUILD #define printf BREAKTHECOMPILE #define wprintf BREAKTHECOMPILE diff --git a/Minecraft.Client/Platform/stubs.h b/Minecraft.Client/Platform/stubs.h index 91464c6a8..9841637ca 100644 --- a/Minecraft.Client/Platform/stubs.h +++ b/Minecraft.Client/Platform/stubs.h @@ -13,30 +13,32 @@ class FloatBuffer; class IntBuffer; class ByteBuffer; -void glGenTextures(IntBuffer *); +void glGenTextures(IntBuffer*); int glGenTextures(); -void glDeleteTextures(IntBuffer *); +void glDeleteTextures(IntBuffer*); void glDeleteTextures(int); -void glLight(int, int, FloatBuffer *); -void glLightModel(int, FloatBuffer *); -void glGetFloat(int, FloatBuffer *); -void glTexGen(int, int, FloatBuffer *); -void glFog(int, FloatBuffer *); -void glTexCoordPointer(int, int, FloatBuffer *); -void glNormalPointer(int, ByteBuffer *); -void glColorPointer(int, bool, int, ByteBuffer *); -void glVertexPointer(int, int, FloatBuffer *); -void glEndList(int); -void glTexImage2D(int, int, int, int, int, int, int, int, ByteBuffer *); -void glCallLists(IntBuffer *); -void glGenQueriesARB(IntBuffer *); +void glLight(int, int, FloatBuffer*); +void glLightModel(int, FloatBuffer*); +void glGetFloat(int, FloatBuffer*); +void glTexGen(int, int, FloatBuffer*); +void glFog(int, FloatBuffer*); +void glTexCoordPointer(int, int, FloatBuffer*); +void glNormalPointer(int, ByteBuffer*); +void glColorPointer(int, bool, int, ByteBuffer*); +void glVertexPointer(int, int, FloatBuffer*); + +void glEndList_4J(int vertexCount = 0); + +void glTexImage2D(int, int, int, int, int, int, int, int, ByteBuffer*); +void glCallLists(IntBuffer*); +void glGenQueriesARB(IntBuffer*); void glBeginQueryARB(int, int); void glEndQueryARB(int); -void glGetQueryObjectuARB(int, int, IntBuffer *); -void glReadPixels(int, int, int, int, int, int, ByteBuffer *); +void glGetQueryObjectuARB(int, int, IntBuffer*); +void glReadPixels(int, int, int, int, int, int, ByteBuffer*); + void LinuxGLLogLightmapState(const char* stage, int textureId, bool scaleLight); void LinuxLogStubLightmapProbe(); - #else const int GL_BYTE = 0; @@ -54,13 +56,9 @@ const int GL_NORMALIZE = 0; const int GL_RESCALE_NORMAL = 0; - - const int GL_SMOOTH = 0; const int GL_FLAT = 0; - - const int GL_RGBA = 0; const int GL_BGRA = 1; const int GL_BGR = 0; @@ -83,73 +81,73 @@ const int GL_TEXTURE1 = 0; const int GL_TEXTURE0 = 1; void glFlush(); -void glTexGeni(int,int,int); -void glTexGen(int,int,FloatBuffer *); -void glReadPixels(int,int, int, int, int, int, ByteBuffer *); +void glTexGeni(int, int, int); +void glTexGen(int, int, FloatBuffer*); +void glReadPixels(int, int, int, int, int, int, ByteBuffer*); void glClearDepth(double); void glCullFace(int); -void glDeleteLists(int,int); -void glGenTextures(IntBuffer *); +void glDeleteLists(int, int); +void glGenTextures(IntBuffer*); int glGenTextures(); int glGenLists(int); -void glLight(int, int,FloatBuffer *); -void glLightModel(int, FloatBuffer *); -void glGetFloat(int a, FloatBuffer *b); +void glLight(int, int, FloatBuffer*); +void glLightModel(int, FloatBuffer*); +void glGetFloat(int a, FloatBuffer* b); void glTexCoordPointer(int, int, int, int); -void glTexCoordPointer(int, int, FloatBuffer *); +void glTexCoordPointer(int, int, FloatBuffer*); void glNormalPointer(int, int, int); -void glNormalPointer(int, ByteBuffer *); +void glNormalPointer(int, ByteBuffer*); void glEnableClientState(int); void glDisableClientState(int); -void glColorPointer(int, bool, int, ByteBuffer *); +void glColorPointer(int, bool, int, ByteBuffer*); void glColorPointer(int, int, int, int); void glVertexPointer(int, int, int, int); -void glVertexPointer(int, int, FloatBuffer *); -void glDrawArrays(int,int,int); -void glTranslatef(float,float,float); -void glRotatef(float,float,float,float); -void glNewList(int,int); +void glVertexPointer(int, int, FloatBuffer*); +void glDrawArrays(int, int, int); +void glTranslatef(float, float, float); +void glRotatef(float, float, float, float); +void glNewList(int, int); void glEndList(int vertexCount = 0); void glCallList(int); void glPopMatrix(); void glPushMatrix(); -void glColor3f(float,float,float); -void glScalef(float,float,float); -void glMultMatrixf(float *); -void glColor4f(float,float,float,float); +void glColor3f(float, float, float); +void glScalef(float, float, float); +void glMultMatrixf(float*); +void glColor4f(float, float, float, float); void glDisable(int); void glEnable(int); -void glBlendFunc(int,int); +void glBlendFunc(int, int); void glDepthMask(bool); -void glNormal3f(float,float,float); +void glNormal3f(float, float, float); void glDepthFunc(int); void glMatrixMode(int); void glLoadIdentity(); -void glBindTexture(int,int); -void glTexParameteri(int,int,int); -void glTexImage2D(int,int,int,int,int,int,int,int,ByteBuffer *); -void glDeleteTextures(IntBuffer *); +void glBindTexture(int, int); +void glTexParameteri(int, int, int); +void glTexImage2D(int, int, int, int, int, int, int, int, ByteBuffer*); +void glDeleteTextures(IntBuffer*); void glDeleteTextures(int); -void glCallLists(IntBuffer *); -void glGenQueriesARB(IntBuffer *); -void glColorMask(bool,bool,bool,bool); -void glBeginQueryARB(int,int); +void glCallLists(IntBuffer*); +void glGenQueriesARB(IntBuffer*); +void glColorMask(bool, bool, bool, bool); +void glBeginQueryARB(int, int); void glEndQueryARB(int); -void glGetQueryObjectuARB(int,int,IntBuffer *); +void glGetQueryObjectuARB(int, int, IntBuffer*); void glShadeModel(int); -void glPolygonOffset(float,float); +void glPolygonOffset(float, float); void glLineWidth(float); -void glScaled(double,double,double); -void gluPerspective(float,float,float,float); +void glScaled(double, double, double); +void gluPerspective(float, float, float, float); void glClear(int); -void glViewport(int,int,int,int); -void glAlphaFunc(int,float); -void glOrtho(float,float,float,float,float,float); -void glClearColor(float,float,float,float); -void glFogi(int,int); -void glFogf(int,float); -void glFog(int,FloatBuffer *); -void glColorMaterial(int,int); +void glViewport(int, int, int, int); +void glAlphaFunc(int, float); +void glOrtho(float, float, float, float, float, float); +void glClearColor(float, float, float, float); +void glFogi(int, int); +void glFogf(int, float); +void glFog(int, FloatBuffer*); +void glColorMaterial(int, int); void glMultiTexCoord2f(int, float, float); void glClientActiveTexture(int); @@ -158,164 +156,150 @@ void glActiveTexture(int); #endif #ifdef __linux__ -class GL11 -{ -public: - static const int GL_SMOOTH = 0x1D01; - static const int GL_FLAT = 0x1D00; - static void glShadeModel(int mode) { ::glShadeModel(mode); } -}; +class GL11 { +public: + static const int GL_SMOOTH = 0x1D01; + static const int GL_FLAT = 0x1D00; +#undef glShadeModel +#define GL_SHADEMODEL_IS_FUNCTION + static void glShadeModel(int mode) { ::glShadeModel(mode); } +}; #undef GL_ARRAY_BUFFER_ARB #undef GL_STREAM_DRAW_ARB -class ARBVertexBufferObject -{ +class ARBVertexBufferObject { public: - static const int GL_ARRAY_BUFFER_ARB = 0x8892; - static const int GL_STREAM_DRAW_ARB = 0x88E0; - static void glBindBufferARB(int, int) {} - static void glBufferDataARB(int, ByteBuffer *, int) {} - static void glGenBuffersARB(IntBuffer *) {} + static const int GL_ARRAY_BUFFER_ARB = 0x8892; + static const int GL_STREAM_DRAW_ARB = 0x88E0; + static void glBindBufferARB(int, int) {} + static void glBufferDataARB(int, ByteBuffer*, int) {} + static void glGenBuffersARB(IntBuffer*) {} }; #else -class GL11 -{ +class GL11 { public: - static const int GL_SMOOTH = 0; - static const int GL_FLAT = 0; - static void glShadeModel(int) {}; + static const int GL_SMOOTH = 0; + static const int GL_FLAT = 0; + static void glShadeModel(int) {}; }; -class ARBVertexBufferObject -{ +class ARBVertexBufferObject { public: - static const int GL_ARRAY_BUFFER_ARB = 0; - static const int GL_STREAM_DRAW_ARB = 0; - static void glBindBufferARB(int, int) {} - static void glBufferDataARB(int, ByteBuffer *, int) {} - static void glGenBuffersARB(IntBuffer *) {} + static const int GL_ARRAY_BUFFER_ARB = 0; + static const int GL_STREAM_DRAW_ARB = 0; + static void glBindBufferARB(int, int) {} + static void glBufferDataARB(int, ByteBuffer*, int) {} + static void glGenBuffersARB(IntBuffer*) {} }; #endif - class Level; class Player; class Textures; class Font; class MapItemSavedData; class Mob; -class Particles -{ +class Particles { public: - void render(float) {} - void tick() {} + void render(float) {} + void tick() {} }; class BufferedImage; -class Graphics -{ +class Graphics { public: - void drawImage(BufferedImage *, int, int, void *) {} - void dispose() {} + void drawImage(BufferedImage*, int, int, void*) {} + void dispose() {} }; -class ZipEntry -{ -}; +class ZipEntry {}; class InputStream; class File; -class ZipFile -{ +class ZipFile { public: - ZipFile(File *file) {} - InputStream *getInputStream(ZipEntry *entry) { return NULL; } - ZipEntry *getEntry(const std::wstring& name) {return NULL;} - void close() {} + ZipFile(File* file) {} + InputStream* getInputStream(ZipEntry* entry) { return NULL; } + ZipEntry* getEntry(const std::wstring& name) { return NULL; } + void close() {} }; -class ImageIO -{ +class ImageIO { public: - static BufferedImage *read(InputStream *in) { return NULL; } + static BufferedImage* read(InputStream* in) { return NULL; } }; -class Keyboard -{ +class Keyboard { public: - static void create() {} - static void destroy() {} - static bool isKeyDown(int) {return false;} - static std::wstring getKeyName(int) { return L"KEYNAME"; } - static void enableRepeatEvents(bool) {} - static const int KEY_A = 0; - static const int KEY_B = 1; - static const int KEY_C = 2; - static const int KEY_D = 3; - static const int KEY_E = 4; - static const int KEY_F = 5; - static const int KEY_G = 6; - static const int KEY_H = 7; - static const int KEY_I = 8; - static const int KEY_J = 9; - static const int KEY_K = 10; - static const int KEY_L = 11; - static const int KEY_M = 12; - static const int KEY_N = 13; - static const int KEY_O = 14; - static const int KEY_P = 15; - static const int KEY_Q = 16; - static const int KEY_R = 17; - static const int KEY_S = 18; - static const int KEY_T = 19; - static const int KEY_U = 20; - static const int KEY_V = 21; - static const int KEY_W = 22; - static const int KEY_X = 23; - static const int KEY_Y = 24; - static const int KEY_Z = 25; - static const int KEY_SPACE = 26; - static const int KEY_LSHIFT = 27; - static const int KEY_ESCAPE = 28; - static const int KEY_BACK = 29; - static const int KEY_RETURN = 30; - static const int KEY_RSHIFT = 31; - static const int KEY_UP = 32; - static const int KEY_DOWN = 33; - static const int KEY_TAB = 34; + static void create() {} + static void destroy() {} + static bool isKeyDown(int) { return false; } + static std::wstring getKeyName(int) { return L"KEYNAME"; } + static void enableRepeatEvents(bool) {} + static const int KEY_A = 0; + static const int KEY_B = 1; + static const int KEY_C = 2; + static const int KEY_D = 3; + static const int KEY_E = 4; + static const int KEY_F = 5; + static const int KEY_G = 6; + static const int KEY_H = 7; + static const int KEY_I = 8; + static const int KEY_J = 9; + static const int KEY_K = 10; + static const int KEY_L = 11; + static const int KEY_M = 12; + static const int KEY_N = 13; + static const int KEY_O = 14; + static const int KEY_P = 15; + static const int KEY_Q = 16; + static const int KEY_R = 17; + static const int KEY_S = 18; + static const int KEY_T = 19; + static const int KEY_U = 20; + static const int KEY_V = 21; + static const int KEY_W = 22; + static const int KEY_X = 23; + static const int KEY_Y = 24; + static const int KEY_Z = 25; + static const int KEY_SPACE = 26; + static const int KEY_LSHIFT = 27; + static const int KEY_ESCAPE = 28; + static const int KEY_BACK = 29; + static const int KEY_RETURN = 30; + static const int KEY_RSHIFT = 31; + static const int KEY_UP = 32; + static const int KEY_DOWN = 33; + static const int KEY_TAB = 34; }; -class Mouse -{ +class Mouse { public: - static void create() {} - static void destroy() {} - static int getX() { return 0; } - static int getY() { return 0; } - static bool isButtonDown(int) { return false; } + static void create() {} + static void destroy() {} + static int getX() { return 0; } + static int getY() { return 0; } + static bool isButtonDown(int) { return false; } }; -class Display -{ +class Display { public: - static bool isActive() {return true;} - static void update(); - static void swapBuffers(); - static void destroy() {} + static bool isActive() { return true; } + static void update(); + static void swapBuffers(); + static void destroy() {} }; -class BackgroundDownloader -{ +class BackgroundDownloader { public: - BackgroundDownloader(File workDir, Minecraft* minecraft) {} - void start() {} - void halt() {} - void forceReload() {} + BackgroundDownloader(File workDir, Minecraft* minecraft) {} + void start() {} + void halt() {} + void forceReload() {} }; -class Color -{ +class Color { public: - static int HSBtoRGB(float,float,float) {return 0;} + static int HSBtoRGB(float, float, float) { return 0; } }; diff --git a/Minecraft.Client/Rendering/Chunk.cpp b/Minecraft.Client/Rendering/Chunk.cpp index a52ac7459..9b294d5e8 100644 --- a/Minecraft.Client/Rendering/Chunk.cpp +++ b/Minecraft.Client/Rendering/Chunk.cpp @@ -7,6 +7,8 @@ #include "../../Minecraft.World/Headers/net.minecraft.world.level.tile.h" #include "../../Minecraft.World/Headers/net.minecraft.world.level.tile.entity.h" #include "LevelRenderer.h" +#include "../Utils/FrameProfiler.h" +#include #ifdef __PS3__ #include "../Platform/PS3/SPU_Tasks/ChunkUpdate/ChunkRebuildData.h" @@ -35,6 +37,66 @@ Tesselator* Chunk::t = Tesselator::getInstance(); #endif LevelRenderer* Chunk::levelRenderer; +void Chunk::reconcileRenderableTileEntities( + const std::vector >& renderableTileEntities) { + int key = + levelRenderer->getGlobalIndexForChunk(this->x, this->y, this->z, level); + AUTO_VAR(it, globalRenderableTileEntities->find(key)); + if (!renderableTileEntities.empty()) { + std::unordered_set currentRenderableTileEntitySet; + currentRenderableTileEntitySet.reserve(renderableTileEntities.size()); + for (size_t i = 0; i < renderableTileEntities.size(); i++) { + currentRenderableTileEntitySet.insert(renderableTileEntities[i].get()); + } + + if (it != globalRenderableTileEntities->end()) { + LevelRenderer::RenderableTileEntityBucket& existingBucket = + it->second; + + for (AUTO_VAR(it2, existingBucket.tiles.begin()); + it2 != existingBucket.tiles.end(); it2++) { + TileEntity* tileEntity = (*it2).get(); + if (currentRenderableTileEntitySet.find(tileEntity) == + currentRenderableTileEntitySet.end()) { + (*it2)->setRenderRemoveStage( + TileEntity::e_RenderRemoveStageFlaggedAtChunk); + levelRenderer->queueRenderableTileEntityForRemoval_Locked( + key, tileEntity); + } else { + (*it2)->setRenderRemoveStage( + TileEntity::e_RenderRemoveStageKeep); + } + } + + for (size_t i = 0; i < renderableTileEntities.size(); i++) { + renderableTileEntities[i]->setRenderRemoveStage( + TileEntity::e_RenderRemoveStageKeep); + if (existingBucket.indexByTile.find(renderableTileEntities[i].get()) == + existingBucket.indexByTile.end()) { + levelRenderer->addRenderableTileEntity_Locked( + key, renderableTileEntities[i]); + } + } + } else { + for (size_t i = 0; i < renderableTileEntities.size(); i++) { + renderableTileEntities[i]->setRenderRemoveStage( + TileEntity::e_RenderRemoveStageKeep); + levelRenderer->addRenderableTileEntity_Locked( + key, renderableTileEntities[i]); + } + } + } else if (it != globalRenderableTileEntities->end()) { + for (AUTO_VAR(it2, it->second.tiles.begin()); + it2 != it->second.tiles.end(); + it2++) { + (*it2)->setRenderRemoveStage( + TileEntity::e_RenderRemoveStageFlaggedAtChunk); + levelRenderer->queueRenderableTileEntityForRemoval_Locked(key, + (*it2).get()); + } + } +} + // TODO - 4J see how input entity vector is set up and decide what way is best // to pass this to the function Chunk::Chunk(Level* level, LevelRenderer::rteMap& globalRenderableTileEntities, @@ -72,6 +134,7 @@ void Chunk::setPos(int x, int y, int z) { clipChunk->globalIdx = LevelRenderer::getGlobalIndexForChunk(x, y, z, level); + levelRenderer->setGlobalChunkConnectivity(clipChunk->globalIdx, ~0ULL); #if 1 // 4J - we're not using offsetted renderlists anymore, so just set the full @@ -252,94 +315,97 @@ void Chunk::rebuild() { // into this category. By far the largest category of these are tiles in // solid regions of rock. bool empty = true; - for (int yy = y0; yy < y1; yy++) { - for (int zz = 0; zz < 16; zz++) { - for (int xx = 0; xx < 16; xx++) { - // 4J Stu - tile data is ordered in 128 blocks of full width, - // lower 128 then upper 128 - int indexY = yy; - int offset = 0; - if (indexY >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) { - indexY -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; - offset = Level::COMPRESSED_CHUNK_SECTION_TILES; - } - - unsigned char tileId = - tileIds[offset + (((xx + 0) << 11) | ((zz + 0) << 7) | - (indexY + 0))]; - if (tileId > 0) empty = false; - - // Don't bother trying to work out neighbours for this tile if - // we are at the edge of the chunk - apart from the very bottom - // of the world where we shouldn't ever be able to see - if (yy == (Level::maxBuildHeight - 1)) continue; - if ((xx == 0) || (xx == 15)) continue; - if ((zz == 0) || (zz == 15)) continue; - - // Establish whether this tile and its neighbours are all made - // of rock, dirt, unbreakable tiles, or have already been - // determined to meet this criteria themselves and have a tile - // of 255 set. - if (!((tileId == Tile::stone_Id) || (tileId == Tile::dirt_Id) || - (tileId == Tile::unbreakable_Id) || (tileId == 255))) - continue; - tileId = tileIds[offset + (((xx - 1) << 11) | ((zz + 0) << 7) | - (indexY + 0))]; - if (!((tileId == Tile::stone_Id) || (tileId == Tile::dirt_Id) || - (tileId == Tile::unbreakable_Id) || (tileId == 255))) - continue; - tileId = tileIds[offset + (((xx + 1) << 11) | ((zz + 0) << 7) | - (indexY + 0))]; - if (!((tileId == Tile::stone_Id) || (tileId == Tile::dirt_Id) || - (tileId == Tile::unbreakable_Id) || (tileId == 255))) - continue; - tileId = tileIds[offset + (((xx + 0) << 11) | ((zz - 1) << 7) | - (indexY + 0))]; - if (!((tileId == Tile::stone_Id) || (tileId == Tile::dirt_Id) || - (tileId == Tile::unbreakable_Id) || (tileId == 255))) - continue; - tileId = tileIds[offset + (((xx + 0) << 11) | ((zz + 1) << 7) | - (indexY + 0))]; - if (!((tileId == Tile::stone_Id) || (tileId == Tile::dirt_Id) || - (tileId == Tile::unbreakable_Id) || (tileId == 255))) - continue; - // Treat the bottom of the world differently - we shouldn't ever - // be able to look up at this, so consider tiles as invisible if - // they are surrounded on sides other than the bottom - if (yy > 0) { - int indexYMinusOne = yy - 1; - int yMinusOneOffset = 0; - if (indexYMinusOne >= - Level::COMPRESSED_CHUNK_SECTION_HEIGHT) { - indexYMinusOne -= - Level::COMPRESSED_CHUNK_SECTION_HEIGHT; - yMinusOneOffset = Level::COMPRESSED_CHUNK_SECTION_TILES; + { + FRAME_PROFILE_SCOPE(ChunkPrepass); + for (int yy = y0; yy < y1; yy++) { + for (int zz = 0; zz < 16; zz++) { + for (int xx = 0; xx < 16; xx++) { + // 4J Stu - tile data is ordered in 128 blocks of full width, + // lower 128 then upper 128 + int indexY = yy; + int offset = 0; + if (indexY >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) { + indexY -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + offset = Level::COMPRESSED_CHUNK_SECTION_TILES; } - tileId = tileIds[yMinusOneOffset + (((xx + 0) << 11) | - ((zz + 0) << 7) | - indexYMinusOne)]; - if (!((tileId == Tile::stone_Id) || - (tileId == Tile::dirt_Id) || + + unsigned char tileId = + tileIds[offset + (((xx + 0) << 11) | ((zz + 0) << 7) | + (indexY + 0))]; + if (tileId > 0) empty = false; + + // Don't bother trying to work out neighbours for this tile if + // we are at the edge of the chunk - apart from the very bottom + // of the world where we shouldn't ever be able to see + if (yy == (Level::maxBuildHeight - 1)) continue; + if ((xx == 0) || (xx == 15)) continue; + if ((zz == 0) || (zz == 15)) continue; + + // Establish whether this tile and its neighbours are all made + // of rock, dirt, unbreakable tiles, or have already been + // determined to meet this criteria themselves and have a tile + // of 255 set. + if (!((tileId == Tile::stone_Id) || (tileId == Tile::dirt_Id) || + (tileId == Tile::unbreakable_Id) || (tileId == 255))) + continue; + tileId = tileIds[offset + (((xx - 1) << 11) | ((zz + 0) << 7) | + (indexY + 0))]; + if (!((tileId == Tile::stone_Id) || (tileId == Tile::dirt_Id) || + (tileId == Tile::unbreakable_Id) || (tileId == 255))) + continue; + tileId = tileIds[offset + (((xx + 1) << 11) | ((zz + 0) << 7) | + (indexY + 0))]; + if (!((tileId == Tile::stone_Id) || (tileId == Tile::dirt_Id) || + (tileId == Tile::unbreakable_Id) || (tileId == 255))) + continue; + tileId = tileIds[offset + (((xx + 0) << 11) | ((zz - 1) << 7) | + (indexY + 0))]; + if (!((tileId == Tile::stone_Id) || (tileId == Tile::dirt_Id) || + (tileId == Tile::unbreakable_Id) || (tileId == 255))) + continue; + tileId = tileIds[offset + (((xx + 0) << 11) | ((zz + 1) << 7) | + (indexY + 0))]; + if (!((tileId == Tile::stone_Id) || (tileId == Tile::dirt_Id) || + (tileId == Tile::unbreakable_Id) || (tileId == 255))) + continue; + // Treat the bottom of the world differently - we shouldn't ever + // be able to look up at this, so consider tiles as invisible if + // they are surrounded on sides other than the bottom + if (yy > 0) { + int indexYMinusOne = yy - 1; + int yMinusOneOffset = 0; + if (indexYMinusOne >= + Level::COMPRESSED_CHUNK_SECTION_HEIGHT) { + indexYMinusOne -= + Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + yMinusOneOffset = Level::COMPRESSED_CHUNK_SECTION_TILES; + } + tileId = tileIds[yMinusOneOffset + (((xx + 0) << 11) | + ((zz + 0) << 7) | + indexYMinusOne)]; + if (!((tileId == Tile::stone_Id) || + (tileId == Tile::dirt_Id) || + (tileId == Tile::unbreakable_Id) || (tileId == 255))) + continue; + } + int indexYPlusOne = yy + 1; + int yPlusOneOffset = 0; + if (indexYPlusOne >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) { + indexYPlusOne -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + yPlusOneOffset = Level::COMPRESSED_CHUNK_SECTION_TILES; + } + tileId = + tileIds[yPlusOneOffset + (((xx + 0) << 11) | + ((zz + 0) << 7) | indexYPlusOne)]; + if (!((tileId == Tile::stone_Id) || (tileId == Tile::dirt_Id) || (tileId == Tile::unbreakable_Id) || (tileId == 255))) continue; - } - int indexYPlusOne = yy + 1; - int yPlusOneOffset = 0; - if (indexYPlusOne >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) { - indexYPlusOne -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; - yPlusOneOffset = Level::COMPRESSED_CHUNK_SECTION_TILES; - } - tileId = - tileIds[yPlusOneOffset + (((xx + 0) << 11) | - ((zz + 0) << 7) | indexYPlusOne)]; - if (!((tileId == Tile::stone_Id) || (tileId == Tile::dirt_Id) || - (tileId == Tile::unbreakable_Id) || (tileId == 255))) - continue; - // This tile is surrounded. Flag it as not requiring to be - // rendered by setting its id to 255. - tileIds[offset + (((xx + 0) << 11) | ((zz + 0) << 7) | - (indexY + 0))] = 0xff; + // This tile is surrounded. Flag it as not requiring to be + // rendered by setting its id to 255. + tileIds[offset + (((xx + 0) << 11) | ((zz + 0) << 7) | + (indexY + 0))] = 0xff; + } } } } @@ -354,6 +420,12 @@ void Chunk::rebuild() { RenderManager.CBuffClear(lists + currentLayer); } + int globalIdx = levelRenderer->getGlobalIndexForChunk(this->x, this->y, + this->z, level); + levelRenderer->setGlobalChunkConnectivity(globalIdx, ~0ULL); + levelRenderer->setGlobalChunkFlag(this->x, this->y, this->z, level, + LevelRenderer::CHUNK_FLAG_COMPILED); + delete region; delete tileRenderer; return; @@ -410,20 +482,8 @@ void Chunk::rebuild() { MemSect(31); glNewList(lists + currentLayer, GL_COMPILE); MemSect(0); - glPushMatrix(); glDepthMask(true); // 4J added t->useCompactVertices(true); // 4J added - translateToPos(); - float ss = 1.000001f; - // 4J - have removed this scale as I don't think we - // should need it, and have now optimised the vertex - // shader so it doesn't do anything other than - // translate with this matrix anyway -#if 0 - glTranslatef(-zs / 2.0f, -ys / 2.0f, -zs / 2.0f); - glScalef(ss, ss, ss); - glTranslatef(zs / 2.0f, ys / 2.0f, zs / 2.0f); -#endif t->begin(); t->offset((float)(-this->x), (float)(-this->y), (float)(-this->z)); @@ -471,7 +531,6 @@ void Chunk::rebuild() { #endif t->end(); bounds.addBounds(t->bounds); // 4J MGH - added - glPopMatrix(); glEndList(); t->useCompactVertices(false); // 4J added t->offset(0, 0, 0); @@ -504,6 +563,11 @@ void Chunk::rebuild() { bb = {bounds.boundingBox[0], bounds.boundingBox[1], bounds.boundingBox[2], bounds.boundingBox[3], bounds.boundingBox[4], bounds.boundingBox[5]}; + uint64_t conn = computeConnectivity(tileIds); // pass tileIds + int globalIdx = + levelRenderer->getGlobalIndexForChunk(this->x, this->y, this->z, level); + levelRenderer->setGlobalChunkConnectivity(globalIdx, conn); + delete tileRenderer; delete region; @@ -516,57 +580,8 @@ void Chunk::rebuild() { // from the dimension and chunk position (using same index as is used for // global flags) #if 1 - int key = - levelRenderer->getGlobalIndexForChunk(this->x, this->y, this->z, level); EnterCriticalSection(globalRenderableTileEntities_cs); - if (renderableTileEntities.size()) { - AUTO_VAR(it, globalRenderableTileEntities->find(key)); - if (it != globalRenderableTileEntities->end()) { - // We've got some renderable tile entities that we want associated - // with this chunk, and an existing list of things that used to be. - // We need to flag any that we don't need any more to be removed, - // keep those that we do, and add any new ones - - // First pass - flag everything already existing to be removed - for (AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); - it2++) { - (*it2)->setRenderRemoveStage( - TileEntity::e_RenderRemoveStageFlaggedAtChunk); - } - - // Now go through the current list. If these are already in the - // list, then unflag the remove flag. If they aren't, then add - for (int i = 0; i < renderableTileEntities.size(); i++) { - AUTO_VAR(it2, find(it->second.begin(), it->second.end(), - renderableTileEntities[i])); - if (it2 == it->second.end()) { - (*globalRenderableTileEntities)[key].push_back( - renderableTileEntities[i]); - } else { - (*it2)->setRenderRemoveStage( - TileEntity::e_RenderRemoveStageKeep); - } - } - } else { - // Easy case - nothing already existing for this chunk. Add them all - // in. - for (int i = 0; i < renderableTileEntities.size(); i++) { - (*globalRenderableTileEntities)[key].push_back( - renderableTileEntities[i]); - } - } - } else { - // Another easy case - we don't want any renderable tile entities - // associated with this chunk. Flag all to be removed. - AUTO_VAR(it, globalRenderableTileEntities->find(key)); - if (it != globalRenderableTileEntities->end()) { - for (AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); - it2++) { - (*it2)->setRenderRemoveStage( - TileEntity::e_RenderRemoveStageFlaggedAtChunk); - } - } - } + reconcileRenderableTileEntities(renderableTileEntities); LeaveCriticalSection(globalRenderableTileEntities_cs); PIXEndNamedEvent(); #else @@ -734,19 +749,8 @@ void Chunk::rebuild_SPU() { { glNewList(lists + currentLayer, GL_COMPILE); MemSect(0); - glPushMatrix(); glDepthMask(true); // 4J added t->useCompactVertices(true); // 4J added - translateToPos(); - float ss = 1.000001f; - // 4J - have removed this scale as I don't think we should need it, - // and have now optimised the vertex shader so it doesn't do - // anything other than translate with this matrix anyway -#if 0 - glTranslatef(-zs / 2.0f, -ys / 2.0f, -zs / 2.0f); - glScalef(ss, ss, ss); - glTranslatef(zs / 2.0f, ys / 2.0f, zs / 2.0f); -#endif t->begin(); t->offset((float)(-this->x), (float)(-this->y), (float)(-this->z)); } @@ -776,7 +780,7 @@ void Chunk::rebuild_SPU() { if (currentLayer == 0 && Tile::tiles[tileId]->isEntityTile()) { std::shared_ptr et = - region.getTileEntity(x, y, z); + region->getTileEntity(x, y, z); if (TileEntityRenderDispatcher::instance ->hasRenderer(et)) { renderableTileEntities.push_back(et); @@ -794,7 +798,7 @@ void Chunk::rebuild_SPU() { } else if (renderLayer == currentLayer) { // if(currentLayer == 0) // numRenderedLayer0++; - rendered |= tileRenderer.tesselateInWorld( + rendered |= tileRenderer->tesselateInWorld( tile, x, y, z); } } @@ -806,7 +810,6 @@ void Chunk::rebuild_SPU() { { t->end(); bounds.addBounds(t->bounds); - glPopMatrix(); glEndList(); t->useCompactVertices(false); // 4J added t->offset(0, 0, 0); @@ -840,57 +843,8 @@ void Chunk::rebuild_SPU() { // from the dimension and chunk position (using same index as is used for // global flags) #if 1 - int key = - levelRenderer->getGlobalIndexForChunk(this->x, this->y, this->z, level); EnterCriticalSection(globalRenderableTileEntities_cs); - if (renderableTileEntities.size()) { - AUTO_VAR(it, globalRenderableTileEntities->find(key)); - if (it != globalRenderableTileEntities->end()) { - // We've got some renderable tile entities that we want associated - // with this chunk, and an existing list of things that used to be. - // We need to flag any that we don't need any more to be removed, - // keep those that we do, and add any new ones - - // First pass - flag everything already existing to be removed - for (AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); - it2++) { - (*it2)->setRenderRemoveStage( - TileEntity::e_RenderRemoveStageFlaggedAtChunk); - } - - // Now go through the current list. If these are already in the - // list, then unflag the remove flag. If they aren't, then add - for (int i = 0; i < renderableTileEntities.size(); i++) { - AUTO_VAR(it2, find(it->second.begin(), it->second.end(), - renderableTileEntities[i])); - if (it2 == it->second.end()) { - (*globalRenderableTileEntities)[key].push_back( - renderableTileEntities[i]); - } else { - (*it2)->setRenderRemoveStage( - TileEntity::e_RenderRemoveStageKeep); - } - } - } else { - // Easy case - nothing already existing for this chunk. Add them all - // in. - for (int i = 0; i < renderableTileEntities.size(); i++) { - (*globalRenderableTileEntities)[key].push_back( - renderableTileEntities[i]); - } - } - } else { - // Another easy case - we don't want any renderable tile entities - // associated with this chunk. Flag all to be removed. - AUTO_VAR(it, globalRenderableTileEntities->find(key)); - if (it != globalRenderableTileEntities->end()) { - for (AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); - it2++) { - (*it2)->setRenderRemoveStage( - TileEntity::e_RenderRemoveStageFlaggedAtChunk); - } - } - } + reconcileRenderableTileEntities(renderableTileEntities); LeaveCriticalSection(globalRenderableTileEntities_cs); #else // Find the removed ones: @@ -979,17 +933,157 @@ float Chunk::squishedDistanceToSqr(std::shared_ptr player) { return xd * xd + yd * yd + zd * zd; } +uint64_t Chunk::computeConnectivity(const uint8_t* tileIds) { + const int W = 16; + const int H = 16; + const int VOLUME = W * H * W; + + auto idx = [&](int x, int y, int z) -> int { + return y * W * W + z * W + x; + }; + + auto isOpen = [&](int lx, int ly, int lz) -> bool { + int worldY = this->y + ly; + int offset = 0; + int indexY = worldY; + if (indexY >= Level::COMPRESSED_CHUNK_SECTION_HEIGHT) { + indexY -= Level::COMPRESSED_CHUNK_SECTION_HEIGHT; + offset = Level::COMPRESSED_CHUNK_SECTION_TILES; + } + + uint8_t tileId = tileIds[offset + ((lx << 11) | (lz << 7) | indexY)]; + + if (tileId == 0) return true; // air + if (tileId == 0xFF) return false; // hidden tile (yeah) + + Tile* t = Tile::tiles[tileId]; + return (t == nullptr) || !t->isSolidRender(); + }; + + uint8_t visited[6][512]; + memset(visited, 0, sizeof(visited)); + + static const int FX[6] = {1, -1, 0, 0, 0, 0}; + static const int FY[6] = {0, 0, 1, -1, 0, 0}; + static const int FZ[6] = {0, 0, 0, 0, 1, -1}; + + struct Cell { + int8_t x, y, z; + }; + static thread_local std::vector queue; + + uint64_t result = 0; + + for (int entryFace = 0; entryFace < 6; entryFace++) { + uint8_t* vis = visited[entryFace]; + queue.clear(); + int x0s, x1s, y0s, y1s, z0s, z1s; + switch (entryFace) { + case 0: + x0s = W - 1; + x1s = W - 1; + y0s = 0; + y1s = H - 1; + z0s = 0; + z1s = W - 1; + break; // +X + case 1: + x0s = 0; + x1s = 0; + y0s = 0; + y1s = H - 1; + z0s = 0; + z1s = W - 1; + break; // -X + case 2: + x0s = 0; + x1s = W - 1; + y0s = H - 1; + y1s = H - 1; + z0s = 0; + z1s = W - 1; + break; // +Y + case 3: + x0s = 0; + x1s = W - 1; + y0s = 0; + y1s = 0; + z0s = 0; + z1s = W - 1; + break; // -Y + case 4: + x0s = 0; + x1s = W - 1; + y0s = 0; + y1s = H - 1; + z0s = W - 1; + z1s = W - 1; + break; // +Z + case 5: + x0s = 0; + x1s = W - 1; + y0s = 0; + y1s = H - 1; + z0s = 0; + z1s = 0; + break; // -Z + default: + continue; + } + + for (int sy = y0s; sy <= y1s; sy++) + for (int sz = z0s; sz <= z1s; sz++) + for (int sx = x0s; sx <= x1s; sx++) { + if (!isOpen(sx, sy, sz)) continue; + int i = idx(sx, sy, sz); + if (vis[i >> 3] & (1 << (i & 7))) continue; + vis[i >> 3] |= (1 << (i & 7)); + queue.push_back({(int8_t)sx, (int8_t)sy, (int8_t)sz}); + } + + for (int qi = 0; qi < (int)queue.size(); qi++) { + Cell cur = queue[qi]; + + for (int nb = 0; nb < 6; nb++) { + int nx = cur.x + FX[nb]; + int ny = cur.y + FY[nb]; + int nz = cur.z + FZ[nb]; + + // entry exit conn + if (nx < 0 || nx >= W || ny < 0 || ny >= H || nz < 0 || + nz >= W) { + // nb IS the exit face because FX,FY,FZ are aligned + result |= ((uint64_t)1 << (entryFace * 6 + nb)); + continue; + } + + if (!isOpen(nx, ny, nz)) continue; + + int i = idx(nx, ny, nz); + if (vis[i >> 3] & (1 << (i & 7))) continue; + vis[i >> 3] |= (1 << (i & 7)); + queue.push_back({(int8_t)nx, (int8_t)ny, (int8_t)nz}); + } + } + } + + return result; +} void Chunk::reset() { if (assigned) { + int oldKey = -1; + bool retireRenderableTileEntities = false; + EnterCriticalSection(&levelRenderer->m_csDirtyChunks); + oldKey = levelRenderer->getGlobalIndexForChunk(x, y, z, level); unsigned char refCount = levelRenderer->decGlobalChunkRefCount(x, y, z, level); assigned = false; // printf("\t\t [dec] refcount %d at %d, %d, //%d\n",refCount,x,y,z); - if (refCount == 0) { - int lists = - levelRenderer->getGlobalIndexForChunk(x, y, z, level) * 2; + if (refCount == 0 && oldKey != -1) { + retireRenderableTileEntities = true; + int lists = oldKey * 2; if (lists >= 0) { lists += levelRenderer->chunkLists; for (int i = 0; i < 2; i++) { @@ -1001,6 +1095,10 @@ void Chunk::reset() { } } LeaveCriticalSection(&levelRenderer->m_csDirtyChunks); + + if (retireRenderableTileEntities) { + levelRenderer->retireRenderableTileEntitiesForChunkKey(oldKey); + } } clipChunk->visible = false; @@ -1023,7 +1121,11 @@ int Chunk::getList(int layer) { return -1; } -void Chunk::cull(Culler* culler) { clipChunk->visible = culler->isVisible(&bb); } +void Chunk::cull(Culler* culler) { + if (clipChunk->visible) { + clipChunk->visible = culler->isVisible(&bb); + } +} void Chunk::renderBB() { // glCallList(lists + 2); // 4J - removed - TODO put back in diff --git a/Minecraft.Client/Rendering/Chunk.h b/Minecraft.Client/Rendering/Chunk.h index e6d214ab2..fded430cf 100644 --- a/Minecraft.Client/Rendering/Chunk.h +++ b/Minecraft.Client/Rendering/Chunk.h @@ -48,7 +48,7 @@ public: int xm, ym, zm; AABB bb; ClipChunk* clipChunk; - + uint64_t computeConnectivity(const uint8_t* tileIds); int id; // public: // std::vector > renderableTileEntities; @@ -69,6 +69,8 @@ public: private: void translateToPos(); + void reconcileRenderableTileEntities( + const std::vector >& renderableTileEntities); public: void makeCopyForRebuild(Chunk* source); diff --git a/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp b/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp index bf94c18ab..2b3faece9 100644 --- a/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp +++ b/Minecraft.Client/Rendering/EntityRenderers/ItemRenderer.cpp @@ -361,10 +361,10 @@ void ItemRenderer::renderGuiItem(Font* font, Textures* textures, // 4J - original code left here for reference // 4jcraft: original code reused for proper lighting #if 1 - glTranslatef((float)(x), (float)(y), 0.0f); - glScalef(fScaleX, fScaleY, 1.0f); - glTranslatef(-2.0f,3.0f, -3.0f + blitOffset); - glScalef(10.0f, 10.0f, 10.0f); + glTranslatef((float)(x), (float)(y), 0.0f); + glScalef(fScaleX, fScaleY, 1.0f); + glTranslatef(-2.0f, 3.0f, -3.0f + blitOffset); + glScalef(10.0f, 10.0f, 10.0f); glTranslatef(1.0f, 0.5f, 8.0f); glScalef(1.0f, 1.0f, -1.0f); glRotatef(180.0f + 30.0f, 1.0f, 0.0f, 0.0f); @@ -385,10 +385,11 @@ void ItemRenderer::renderGuiItem(Font* font, Textures* textures, glRotatef(45.0f, 0.0f, 1.0f, 0.0f); // Rotate round y axis (centre at origin) #endif - // 4J-PB - pass the alpha value in - the grass block - // render has the top surface coloured differently to - // the rest of the block - glRotatef(-90.0f, 0.0f, 1.0f, 0.0f); + // 4J-PB - pass the alpha value in - the grass block + // render has the top surface coloured differently to + // the rest of the block + glRotatef(-90.0f, 0.0f, 1.0f, 0.0f); + tileRenderer->renderTile(tile, itemAuxValue, 1, fAlpha, useCompiled); glPopMatrix(); diff --git a/Minecraft.Client/Rendering/EntityRenderers/TileRenderer.cpp b/Minecraft.Client/Rendering/EntityRenderers/TileRenderer.cpp index 3ee4ff71a..f1f91018c 100644 --- a/Minecraft.Client/Rendering/EntityRenderers/TileRenderer.cpp +++ b/Minecraft.Client/Rendering/EntityRenderers/TileRenderer.cpp @@ -11,6 +11,7 @@ #include "../../../Minecraft.World/Headers/net.minecraft.h" #include "../../../Minecraft.World/Headers/net.minecraft.world.h" #include "../Tesselator.h" +#include "../../Utils/FrameProfiler.h" #include "EntityTileRenderer.h" #include "../../GameState/Options.h" @@ -267,7 +268,12 @@ bool TileRenderer::tesselateInWorld( { Tesselator* t = Tesselator::getInstance(); int shape = tt->getRenderShape(); - tt->updateShape(level, x, y, z, forceData, forceEntity); + if (shape == Tile::SHAPE_BLOCK) { + FRAME_PROFILE_SCOPE(ChunkBlockShape); + tt->updateShape(level, x, y, z, forceData, forceEntity); + } else { + tt->updateShape(level, x, y, z, forceData, forceEntity); + } // AP - now that the culling is done earlier we don't need to call setShape // until later on (only for SHAPE_BLOCK) if (shape != Tile::SHAPE_BLOCK) { @@ -278,6 +284,11 @@ bool TileRenderer::tesselateInWorld( bool retVal = false; switch (shape) { case Tile::SHAPE_BLOCK: { + { + FRAME_PROFILE_SCOPE(ChunkBlockShape); + setShape(tt); + } + // 4J - added these faceFlags so we can detect whether this block is // going to have no visible faces and early out the original code // checked noCulling and shouldRenderFace directly where faceFlags @@ -290,6 +301,7 @@ bool TileRenderer::tesselateInWorld( if (noCulling) { faceFlags = 0x3f; } else { + FRAME_PROFILE_SCOPE(ChunkBlockFaceCull); // these block types can take advantage of a faster version of // shouldRenderFace there are others but this is an easy check // which covers the majority Note: This now covers rock, grass, @@ -319,9 +331,6 @@ bool TileRenderer::tesselateInWorld( break; } - // now we need to set the shape - setShape(tt); - retVal = tesselateBlockInWorld(tt, x, y, z, faceFlags); } break; case Tile::SHAPE_TREE: @@ -4828,9 +4837,11 @@ bool TileRenderer::tesselateBlockInWorld(Tile* tt, int x, int y, int z) { if (Tile::lightEmission[tt->id] == 0) // 4J - TODO/remove (Minecraft::useAmbientOcclusion()) { + FRAME_PROFILE_SCOPE(ChunkBlockLighting); return tesselateBlockInWorldWithAmbienceOcclusionTexLighting( tt, x, y, z, r, g, b, 0, smoothShapeLighting); } else { + FRAME_PROFILE_SCOPE(ChunkBlockLighting); return tesselateBlockInWorld(tt, x, y, z, r, g, b); } } @@ -4856,9 +4867,11 @@ bool TileRenderer::tesselateBlockInWorld(Tile* tt, int x, int y, int z, if (Tile::lightEmission[tt->id] == 0) // 4J - TODO/remove (Minecraft::useAmbientOcclusion()) { + FRAME_PROFILE_SCOPE(ChunkBlockLighting); return tesselateBlockInWorldWithAmbienceOcclusionTexLighting( tt, x, y, z, r, g, b, faceFlags, smoothShapeLighting); } else { + FRAME_PROFILE_SCOPE(ChunkBlockLighting); return tesselateBlockInWorld(tt, x, y, z, r, g, b); } } @@ -7049,6 +7062,7 @@ bool TileRenderer::tesselateDoorInWorld(Tile* tt, int x, int y, int z) { void TileRenderer::renderFaceDown(Tile* tt, double x, double y, double z, Icon* tex) { + FRAME_PROFILE_SCOPE(ChunkBlockEmit); Tesselator* t = Tesselator::getInstance(); if (hasFixedTexture()) tex = fixedTexture; @@ -7167,6 +7181,7 @@ void TileRenderer::renderFaceDown(Tile* tt, double x, double y, double z, void TileRenderer::renderFaceUp(Tile* tt, double x, double y, double z, Icon* tex) { + FRAME_PROFILE_SCOPE(ChunkBlockEmit); Tesselator* t = Tesselator::getInstance(); if (hasFixedTexture()) tex = fixedTexture; @@ -7286,6 +7301,7 @@ void TileRenderer::renderFaceUp(Tile* tt, double x, double y, double z, void TileRenderer::renderNorth(Tile* tt, double x, double y, double z, Icon* tex) { + FRAME_PROFILE_SCOPE(ChunkBlockEmit); Tesselator* t = Tesselator::getInstance(); if (hasFixedTexture()) tex = fixedTexture; @@ -7410,6 +7426,7 @@ void TileRenderer::renderNorth(Tile* tt, double x, double y, double z, void TileRenderer::renderSouth(Tile* tt, double x, double y, double z, Icon* tex) { + FRAME_PROFILE_SCOPE(ChunkBlockEmit); Tesselator* t = Tesselator::getInstance(); if (hasFixedTexture()) tex = fixedTexture; @@ -7534,6 +7551,7 @@ void TileRenderer::renderSouth(Tile* tt, double x, double y, double z, void TileRenderer::renderWest(Tile* tt, double x, double y, double z, Icon* tex) { + FRAME_PROFILE_SCOPE(ChunkBlockEmit); Tesselator* t = Tesselator::getInstance(); if (hasFixedTexture()) tex = fixedTexture; @@ -7658,6 +7676,7 @@ void TileRenderer::renderWest(Tile* tt, double x, double y, double z, void TileRenderer::renderEast(Tile* tt, double x, double y, double z, Icon* tex) { + FRAME_PROFILE_SCOPE(ChunkBlockEmit); Tesselator* t = Tesselator::getInstance(); if (hasFixedTexture()) tex = fixedTexture; diff --git a/Minecraft.Client/Rendering/Frustum.cpp b/Minecraft.Client/Rendering/Frustum.cpp index 1144a7d5f..3d2e562ea 100644 --- a/Minecraft.Client/Rendering/Frustum.cpp +++ b/Minecraft.Client/Rendering/Frustum.cpp @@ -1,9 +1,11 @@ #include "../Platform/stdafx.h" #include "../../Minecraft.World/IO/Streams/FloatBuffer.h" #include "Frustum.h" +#include "Camera.h" Frustum* Frustum::frustum = new Frustum(); +// those are now unused but i still gotta do testing. Frustum::Frustum() { _proj = MemoryTracker::createFloatBuffer(16); _modl = MemoryTracker::createFloatBuffer(16); @@ -43,63 +45,38 @@ void Frustum::normalizePlane(float** frustum, int side) { } void Frustum::calculateFrustum() { - _proj->clear(); - _modl->clear(); - _clip->clear(); + // 4jcraft: GL 3.3 core removed GL_MODELVIEW_MATRIX / GL_PROJECTION_MATRIX + // queries. + // Camera::prepare() already captures both matrices every frame :) + // i spent an ungodly amount of time on this simple fix. + memcpy(proj.data, RenderManager.MatrixGet(GL_PROJECTION_MATRIX), + 16 * sizeof(float)); + memcpy(modl.data, RenderManager.MatrixGet(GL_MODELVIEW_MATRIX), + 16 * sizeof(float)); - // glGetFloatv() is used to extract information about our OpenGL world. - // Below, we pass in GL_PROJECTION_MATRIX to abstract our projection matrix. - // It then stores the matrix into an array of [16]. - glGetFloat(GL_PROJECTION_MATRIX, _proj); + float* p = proj.data; + float* m = modl.data; + float* c = clip.data; - // By passing in GL_MODELVIEW_MATRIX, we can abstract our model view matrix. - // This also stores it in an array of [16]. - glGetFloat(GL_MODELVIEW_MATRIX, _modl); + c[0] = m[0] * p[0] + m[1] * p[4] + m[2] * p[8] + m[3] * p[12]; + c[1] = m[0] * p[1] + m[1] * p[5] + m[2] * p[9] + m[3] * p[13]; + c[2] = m[0] * p[2] + m[1] * p[6] + m[2] * p[10] + m[3] * p[14]; + c[3] = m[0] * p[3] + m[1] * p[7] + m[2] * p[11] + m[3] * p[15]; - _proj->flip()->limit(16); - _proj->get(&proj); - _modl->flip()->limit(16); - _modl->get(&modl); + c[4] = m[4] * p[0] + m[5] * p[4] + m[6] * p[8] + m[7] * p[12]; + c[5] = m[4] * p[1] + m[5] * p[5] + m[6] * p[9] + m[7] * p[13]; + c[6] = m[4] * p[2] + m[5] * p[6] + m[6] * p[10] + m[7] * p[14]; + c[7] = m[4] * p[3] + m[5] * p[7] + m[6] * p[11] + m[7] * p[15]; - // Now that we have our modelview and projection matrix, if we combine these - // 2 matrices, it will give us our clipping planes. To combine 2 matrices, - // we multiply them. + c[8] = m[8] * p[0] + m[9] * p[4] + m[10] * p[8] + m[11] * p[12]; + c[9] = m[8] * p[1] + m[9] * p[5] + m[10] * p[9] + m[11] * p[13]; + c[10] = m[8] * p[2] + m[9] * p[6] + m[10] * p[10] + m[11] * p[14]; + c[11] = m[8] * p[3] + m[9] * p[7] + m[10] * p[11] + m[11] * p[15]; - clip[0] = modl[0] * proj[0] + modl[1] * proj[4] + modl[2] * proj[8] + - modl[3] * proj[12]; - clip[1] = modl[0] * proj[1] + modl[1] * proj[5] + modl[2] * proj[9] + - modl[3] * proj[13]; - clip[2] = modl[0] * proj[2] + modl[1] * proj[6] + modl[2] * proj[10] + - modl[3] * proj[14]; - clip[3] = modl[0] * proj[3] + modl[1] * proj[7] + modl[2] * proj[11] + - modl[3] * proj[15]; - - clip[4] = modl[4] * proj[0] + modl[5] * proj[4] + modl[6] * proj[8] + - modl[7] * proj[12]; - clip[5] = modl[4] * proj[1] + modl[5] * proj[5] + modl[6] * proj[9] + - modl[7] * proj[13]; - clip[6] = modl[4] * proj[2] + modl[5] * proj[6] + modl[6] * proj[10] + - modl[7] * proj[14]; - clip[7] = modl[4] * proj[3] + modl[5] * proj[7] + modl[6] * proj[11] + - modl[7] * proj[15]; - - clip[8] = modl[8] * proj[0] + modl[9] * proj[4] + modl[10] * proj[8] + - modl[11] * proj[12]; - clip[9] = modl[8] * proj[1] + modl[9] * proj[5] + modl[10] * proj[9] + - modl[11] * proj[13]; - clip[10] = modl[8] * proj[2] + modl[9] * proj[6] + modl[10] * proj[10] + - modl[11] * proj[14]; - clip[11] = modl[8] * proj[3] + modl[9] * proj[7] + modl[10] * proj[11] + - modl[11] * proj[15]; - - clip[12] = modl[12] * proj[0] + modl[13] * proj[4] + modl[14] * proj[8] + - modl[15] * proj[12]; - clip[13] = modl[12] * proj[1] + modl[13] * proj[5] + modl[14] * proj[9] + - modl[15] * proj[13]; - clip[14] = modl[12] * proj[2] + modl[13] * proj[6] + modl[14] * proj[10] + - modl[15] * proj[14]; - clip[15] = modl[12] * proj[3] + modl[13] * proj[7] + modl[14] * proj[11] + - modl[15] * proj[15]; + c[12] = m[12] * p[0] + m[13] * p[4] + m[14] * p[8] + m[15] * p[12]; + c[13] = m[12] * p[1] + m[13] * p[5] + m[14] * p[9] + m[15] * p[13]; + c[14] = m[12] * p[2] + m[13] * p[6] + m[14] * p[10] + m[15] * p[14]; + c[15] = m[12] * p[3] + m[13] * p[7] + m[14] * p[11] + m[15] * p[15]; // Now we actually want to get the sides of the frustum. To do this we take // the clipping planes we received above and extract the sides from them. diff --git a/Minecraft.Client/Rendering/FrustumData.cpp b/Minecraft.Client/Rendering/FrustumData.cpp index a5f11b768..140fa4772 100644 --- a/Minecraft.Client/Rendering/FrustumData.cpp +++ b/Minecraft.Client/Rendering/FrustumData.cpp @@ -1,11 +1,13 @@ #include "../Platform/stdafx.h" #include "FrustumData.h" -float** m_Frustum; +// float** m_Frustum; FrustumData::FrustumData() { - m_Frustum = new float*[16]; - for (int i = 0; i < 16; i++) m_Frustum[i] = new float[16]; + this->m_Frustum = new float*[6]; + for (int i = 0; i < 6; i++) { + this->m_Frustum[i] = new float[4]; + } proj = floatArray(16); modl = floatArray(16); clip = floatArray(16); @@ -15,7 +17,7 @@ FrustumData::~FrustumData() { delete[] proj.data; delete[] modl.data; delete[] clip.data; - for (int i = 0; i < 16; i++) delete[] m_Frustum[i]; + for (int i = 0; i < 6; i++) delete[] m_Frustum[i]; delete[] m_Frustum; } @@ -128,4 +130,4 @@ bool FrustumData::cubeInFrustum(double x1, double y1, double z1, double x2, bool FrustumData::isVisible(AABB* aabb) { return cubeInFrustum(aabb->x0, aabb->y0, aabb->z0, aabb->x1, aabb->y1, aabb->z1); -} \ No newline at end of file +} diff --git a/Minecraft.Client/Rendering/GameRenderer.cpp b/Minecraft.Client/Rendering/GameRenderer.cpp index 3e5763087..6a2272f7c 100644 --- a/Minecraft.Client/Rendering/GameRenderer.cpp +++ b/Minecraft.Client/Rendering/GameRenderer.cpp @@ -50,6 +50,7 @@ #include "../Textures/Packs/TexturePackRepository.h" #include "../Textures/Packs/TexturePack.h" #include "../Textures/TextureAtlas.h" +#include "../Utils/FrameProfiler.h" bool GameRenderer::anaglyph3d = false; int GameRenderer::anaglyphPass = 0; @@ -72,6 +73,9 @@ ResourceLocation GameRenderer::RAIN_LOCATION = ResourceLocation GameRenderer::SNOW_LOCATION = ResourceLocation(TN_ENVIRONMENT_SNOW); +// dirty light tracking +static bool s_lightTexDirty[XUSER_MAX_COUNT] = {true, true, true, true}; + GameRenderer::GameRenderer(Minecraft* mc) { // 4J - added this block of initialisers renderDistance = 0; @@ -773,6 +777,7 @@ void GameRenderer::renderItemInHand(float a, int eye) { // 4J - change brought forward from 1.8.2 void GameRenderer::turnOffLightLayer(double alpha) { // 4J - TODO + FRAME_PROFILE_SCOPE(Lightmap); #ifdef __linux__ if (SharedConstants::TEXTURE_LIGHTING) { LinuxLogStubLightmapProbe(); @@ -804,6 +809,7 @@ void GameRenderer::turnOffLightLayer(double alpha) { // 4J - TODO void GameRenderer::turnOnLightLayer( double alpha, bool scaleLight) { // 4jcraft: added scaleLight for entity lighting + FRAME_PROFILE_SCOPE(Lightmap); #ifdef __linux__ if (!SharedConstants::TEXTURE_LIGHTING) return; @@ -871,9 +877,14 @@ void GameRenderer::tickLightTexture() { blr += (blrt - blr) * 1; blg += (blgt - blg) * 1; _updateLightTexture = true; + + // Mark all players dirty so updateLightTexture() knows when it actually + // needs to tick, preventz unessesary player recompute + for (int j = 0; j < XUSER_MAX_COUNT; j++) s_lightTexDirty[j] = true; } void GameRenderer::updateLightTexture(float a) { + FRAME_PROFILE_SCOPE(Lightmap); // 4J-JEV: Now doing light textures on PER PLAYER basis. // 4J - we *had* added separate light textures for all dimensions, and this // loop to update them all here @@ -883,8 +894,10 @@ void GameRenderer::updateLightTexture(float a) { Minecraft::GetInstance()->localplayers[j]; if (player == NULL) continue; - Level* level = player->level; // 4J - was mc->level when it was just to - // update the one light texture + if (!s_lightTexDirty[j]) continue; + s_lightTexDirty[j] = false; + + Level* level = player->level; float skyDarken1 = level->getSkyDarken((float)1); for (int i = 0; i < 256; i++) { @@ -964,10 +977,10 @@ void GameRenderer::updateLightTexture(float a) { _b = _b * 0.96f + 0.03f; if (_r > 1) _r = 1; - if (_g > 1) _g = 1; - if (_b > 1) _b = 1; if (_r < 0) _r = 0; + if (_g > 1) _g = 1; if (_g < 0) _g = 0; + if (_b > 1) _b = 1; if (_b < 0) _b = 0; int alpha = 255; @@ -1016,6 +1029,8 @@ int GameRenderer::getLightTexture(int iPad, Level* level) { } void GameRenderer::render(float a, bool bFirst) { + FRAME_PROFILE_FRAME_SCOPE(); + if (_updateLightTexture && bFirst) updateLightTexture(a); if (Display::isActive()) { lastActiveTime = System::currentTimeMillis(); @@ -1074,6 +1089,7 @@ void GameRenderer::render(float a, bool bFirst) { lastNsTime = System::nanoTime(); if (!mc->options->hideGui || mc->screen != NULL) { + FRAME_PROFILE_SCOPE(UIHud); mc->gui->render(a, mc->screen != NULL, xMouse, yMouse); } } else { @@ -1088,6 +1104,7 @@ void GameRenderer::render(float a, bool bFirst) { } if (mc->screen != NULL) { + FRAME_PROFILE_SCOPE(UIHud); glClear(GL_DEPTH_BUFFER_BIT); mc->screen->render(xMouse, yMouse, a); if (mc->screen != NULL && mc->screen->particles != NULL) @@ -1180,24 +1197,19 @@ int GameRenderer::runUpdate(void* lpParam) { // We've got stacks for things that can only safely be deleted whilst // this thread isn't updating things - delete those things now EnterCriticalSection(&m_csDeleteStack); - for (unsigned int i = 0; i < m_deleteStackByte.size(); i++) { + for (unsigned int i = 0; i < m_deleteStackByte.size(); i++) delete m_deleteStackByte[i]; - } m_deleteStackByte.clear(); for (unsigned int i = 0; i < m_deleteStackSparseLightStorage.size(); - i++) { + i++) delete m_deleteStackSparseLightStorage[i]; - } m_deleteStackSparseLightStorage.clear(); for (unsigned int i = 0; i < m_deleteStackCompressedTileStorage.size(); - i++) { + i++) delete m_deleteStackCompressedTileStorage[i]; - } m_deleteStackCompressedTileStorage.clear(); - for (unsigned int i = 0; i < m_deleteStackSparseDataStorage.size(); - i++) { + for (unsigned int i = 0; i < m_deleteStackSparseDataStorage.size(); i++) delete m_deleteStackSparseDataStorage[i]; - } m_deleteStackSparseDataStorage.clear(); LeaveCriticalSection(&m_csDeleteStack); @@ -1240,6 +1252,8 @@ void GameRenderer::DisableUpdateThread() { } void GameRenderer::renderLevel(float a, int64_t until) { + FRAME_PROFILE_SCOPE(World); + // if (updateLightTexture) updateLightTexture(); // 4J - TODO - // Java 1.0.1 has this line enabled, should check why - don't want to put it // in now in case it breaks split-screen @@ -1292,9 +1306,12 @@ void GameRenderer::renderLevel(float a, int64_t until) { Frustum::getFrustum(); if (mc->options->viewDistance < 2) { setupFog(-1, a); - levelRenderer->renderSky(a); - if (mc->skins->getSelected()->getId() == 1026) - levelRenderer->renderHaloRing(a); + { + FRAME_PROFILE_SCOPE(WeatherSky); + levelRenderer->renderSky(a); + if (mc->skins->getSelected()->getId() == 1026) + levelRenderer->renderHaloRing(a); + } } // 4jcraft: needs to be enabled for proper transparent texturing on low // render dists this was done in renderSky() for the far and normal @@ -1316,7 +1333,10 @@ void GameRenderer::renderLevel(float a, int64_t until) { MemSect(0); frustum->prepare(xOff, yOff, zOff); - mc->levelRenderer->cull(frustum, a); + { + FRAME_PROFILE_SCOPE(ChunkCull); + mc->levelRenderer->cull(frustum, a); + } PIXEndNamedEvent(); #ifndef MULTITHREAD_ENABLE @@ -1343,6 +1363,7 @@ void GameRenderer::renderLevel(float a, int64_t until) { #endif if (cameraEntity->y < Level::genDepth) { + FRAME_PROFILE_SCOPE(WeatherSky); prepareAndRenderClouds(levelRenderer, a); } Frustum::getFrustum(); // 4J added - re-calculate frustum as rendering @@ -1381,7 +1402,10 @@ void GameRenderer::renderLevel(float a, int64_t until) { cameraPos.x = cameraPosTemp.x; cameraPos.y = cameraPosTemp.y; cameraPos.z = cameraPosTemp.z; - levelRenderer->renderEntities(&cameraPos, frustum, a); + { + FRAME_PROFILE_SCOPE(Entity); + levelRenderer->renderEntities(&cameraPos, frustum, a); + } #ifdef __PSVITA__ // AP - make sure we're using the Alpha cut out effect for particles glEnable(GL_ALPHA_TEST); @@ -1389,12 +1413,18 @@ void GameRenderer::renderLevel(float a, int64_t until) { PIXEndNamedEvent(); PIXBeginNamedEvent(0, "Particle render"); turnOnLightLayer(a); // 4J - brought forward from 1.8.2 - particleEngine->renderLit(cameraEntity, a, - ParticleEngine::OPAQUE_LIST); + { + FRAME_PROFILE_SCOPE(Particle); + particleEngine->renderLit(cameraEntity, a, + ParticleEngine::OPAQUE_LIST); + } Lighting::turnOff(); setupFog(0, a); - particleEngine->render(cameraEntity, a, - ParticleEngine::OPAQUE_LIST); + { + FRAME_PROFILE_SCOPE(Particle); + particleEngine->render(cameraEntity, a, + ParticleEngine::OPAQUE_LIST); + } PIXEndNamedEvent(); turnOffLightLayer(a); // 4J - brought forward from 1.8.2 @@ -1463,12 +1493,18 @@ void GameRenderer::renderLevel(float a, int64_t until) { PIXBeginNamedEvent(0, "Particle render (translucent)"); Lighting::turnOn(); turnOnLightLayer(a); // 4J - brought forward from 1.8.2 - particleEngine->renderLit(cameraEntity, a, - ParticleEngine::TRANSLUCENT_LIST); + { + FRAME_PROFILE_SCOPE(Particle); + particleEngine->renderLit(cameraEntity, a, + ParticleEngine::TRANSLUCENT_LIST); + } Lighting::turnOff(); setupFog(0, a); - particleEngine->render(cameraEntity, a, - ParticleEngine::TRANSLUCENT_LIST); + { + FRAME_PROFILE_SCOPE(Particle); + particleEngine->render(cameraEntity, a, + ParticleEngine::TRANSLUCENT_LIST); + } PIXEndNamedEvent(); turnOffLightLayer(a); // 4J - brought forward from 1.8.2 ////////////////////////// End of 4J added section @@ -1499,12 +1535,16 @@ void GameRenderer::renderLevel(float a, int64_t until) { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE); - levelRenderer->renderDestroyAnimation( - Tesselator::getInstance(), - std::dynamic_pointer_cast(cameraEntity), a); + { + FRAME_PROFILE_SCOPE(WeatherSky); + levelRenderer->renderDestroyAnimation( + Tesselator::getInstance(), + std::dynamic_pointer_cast(cameraEntity), a); + } glDisable(GL_BLEND); if (cameraEntity->y >= Level::genDepth) { + FRAME_PROFILE_SCOPE(WeatherSky); prepareAndRenderClouds(levelRenderer, a); } @@ -1513,7 +1553,10 @@ void GameRenderer::renderLevel(float a, int64_t until) { setupFog(0, a); glEnable(GL_FOG); PIXBeginNamedEvent(0, "Rendering snow and rain"); - renderSnowAndRain(a); + { + FRAME_PROFILE_SCOPE(WeatherSky); + renderSnowAndRain(a); + } PIXEndNamedEvent(); glDisable(GL_FOG); @@ -1700,135 +1743,144 @@ void GameRenderer::renderSnowAndRain(float a) { glColor4f(1, 1, 1, 1); - for (int x = x0 - r; x <= x0 + r; x++) + // two snow/rain rendering + mc->textures->bindTexture(&RAIN_LOCATION); + t->begin(); + for (int x = x0 - r; x <= x0 + r; x++) { for (int z = z0 - r; z <= z0 + r; z++) { int rainSlot = (z - z0 + 16) * 32 + (x - x0 + 16); float xa = rainXa[rainSlot] * 0.5f; float za = rainZa[rainSlot] * 0.5f; - // 4J - changes here brought forward from 1.8.2 Biome* b = level->getBiome(x, z); if (!b->hasRain() && !b->hasSnow()) continue; int floor = level->getTopRainBlock(x, z); - int yy0 = y0 - r; int yy1 = y0 + r; - if (yy0 < floor) yy0 = floor; if (yy1 < floor) yy1 = floor; - float s = 1; + if (yy0 == yy1) continue; int yl = floor; if (yl < yMin) yl = yMin; - if (yy0 != yy1) { - random->setSeed((x * x * 3121 + x * 45238971) ^ - (z * z * 418711 + z * 13761)); + float temp = b->getTemperature(); + if (level->getBiomeSource()->scaleTemp(temp, floor) < 0.15f) + continue; - // 4J - changes here brought forward from 1.8.2 - float temp = b->getTemperature(); - if (level->getBiomeSource()->scaleTemp(temp, floor) >= 0.15f) { - if (mode != 0) { - if (mode >= 0) t->end(); - mode = 0; - mc->textures->bindTexture(&RAIN_LOCATION); - t->begin(); - } + random->setSeed((x * x * 3121 + x * 45238971) ^ + (z * z * 418711 + z * 13761)); - float ra = (((_tick + x * x * 3121 + x * 45238971 + - z * z * 418711 + z * 13761) & - 31) + - a) / - 32.0f * (3 + random->nextFloat()); + float ra = (((_tick + x * x * 3121 + x * 45238971 + z * z * 418711 + + z * 13761) & + 31) + + a) / + 32.0f * (3 + random->nextFloat()); - double xd = (x + 0.5f) - player->x; - double zd = (z + 0.5f) - player->z; - float dd = (float)Mth::sqrt(xd * xd + zd * zd) / r; + double xd = (x + 0.5f) - player->x; + double zd = (z + 0.5f) - player->z; + float dd = (float)Mth::sqrt(xd * xd + zd * zd) / r; - float br = 1; - t->offset(-xo * 1, -yo * 1, -zo * 1); + float br = 1.0f; + float s = 1.0f; + t->offset(-xo, -yo, -zo); #ifdef __PSVITA__ - // AP - this will set up the 4 vertices in half the time - float Alpha = ((1 - dd * dd) * 0.5f + 0.5f) * rainLevel; - int tex2 = - (level->getLightColor(x, yl, z, 0) * 3 + 0xf000f0) / 4; - t->tileRainQuad( - x - xa + 0.5, yy0, z - za + 0.5, 0 * s, - yy0 * s / 4.0f + ra * s, x + xa + 0.5, yy0, - z + za + 0.5, 1 * s, yy0 * s / 4.0f + ra * s, - x + xa + 0.5, yy1, z + za + 0.5, 1 * s, - yy1 * s / 4.0f + ra * s, x - xa + 0.5, yy1, - z - za + 0.5, 0 * s, yy1 * s / 4.0f + ra * s, br, br, - br, Alpha, br, br, br, 0, tex2); + float Alpha = ((1 - dd * dd) * 0.5f + 0.5f) * rainLevel; + int tex2 = (level->getLightColor(x, yl, z, 0) * 3 + 0xf000f0) / 4; + t->tileRainQuad( + x - xa + 0.5, yy0, z - za + 0.5, 0 * s, yy0 * s / 4.0f + ra * s, + x + xa + 0.5, yy0, z + za + 0.5, 1 * s, yy0 * s / 4.0f + ra * s, + x + xa + 0.5, yy1, z + za + 0.5, 1 * s, yy1 * s / 4.0f + ra * s, + x - xa + 0.5, yy1, z - za + 0.5, 0 * s, yy1 * s / 4.0f + ra * s, + br, br, br, Alpha, br, br, br, 0, tex2); #else - t->tex2(level->getLightColor(x, yl, z, 0)); - t->color(br, br, br, - ((1 - dd * dd) * 0.5f + 0.5f) * rainLevel); - t->vertexUV(x - xa + 0.5, yy0, z - za + 0.5, 0 * s, - yy0 * s / 4.0f + ra * s); - t->vertexUV(x + xa + 0.5, yy0, z + za + 0.5, 1 * s, - yy0 * s / 4.0f + ra * s); - // 4jcraft: this color call made rain invisible - // t->color(br, br, br, 0.0f); - // // 4J - added to soften the top visible edge of the rain - t->vertexUV(x + xa + 0.5, yy1, z + za + 0.5, 1 * s, - yy1 * s / 4.0f + ra * s); - t->vertexUV(x - xa + 0.5, yy1, z - za + 0.5, 0 * s, - yy1 * s / 4.0f + ra * s); + t->tex2(level->getLightColor(x, yl, z, 0)); + t->color(br, br, br, ((1 - dd * dd) * 0.5f + 0.5f) * rainLevel); + t->vertexUV(x - xa + 0.5, yy0, z - za + 0.5, 0 * s, + yy0 * s / 4.0f + ra * s); + t->vertexUV(x + xa + 0.5, yy0, z + za + 0.5, 1 * s, + yy0 * s / 4.0f + ra * s); + t->vertexUV(x + xa + 0.5, yy1, z + za + 0.5, 1 * s, + yy1 * s / 4.0f + ra * s); + t->vertexUV(x - xa + 0.5, yy1, z - za + 0.5, 0 * s, + yy1 * s / 4.0f + ra * s); #endif - t->offset(0, 0, 0); - t->end(); - } else { - if (mode != 1) { - if (mode >= 0) t->end(); - mode = 1; - mc->textures->bindTexture(&SNOW_LOCATION); - t->begin(); - } - float ra = (((_tick) & 511) + a) / 512.0f; - float uo = random->nextFloat() + - time * 0.01f * (float)random->nextGaussian(); - float vo = random->nextFloat() + - time * (float)random->nextGaussian() * 0.001f; - double xd = (x + 0.5f) - player->x; - double zd = (z + 0.5f) - player->z; - float dd = (float)sqrt(xd * xd + zd * zd) / r; - float br = 1; - t->offset(-xo * 1, -yo * 1, -zo * 1); -#ifdef __PSVITA__ - // AP - this will set up the 4 vertices in half the time - float Alpha = ((1 - dd * dd) * 0.3f + 0.5f) * rainLevel; - int tex2 = - (level->getLightColor(x, yl, z, 0) * 3 + 0xf000f0) / 4; - t->tileRainQuad( - x - xa + 0.5, yy0, z - za + 0.5, 0 * s + uo, - yy0 * s / 4.0f + ra * s + vo, x + xa + 0.5, yy0, - z + za + 0.5, 1 * s + uo, yy0 * s / 4.0f + ra * s + vo, - x + xa + 0.5, yy1, z + za + 0.5, 1 * s + uo, - yy1 * s / 4.0f + ra * s + vo, x - xa + 0.5, yy1, - z - za + 0.5, 0 * s + uo, yy1 * s / 4.0f + ra * s + vo, - br, br, br, Alpha, br, br, br, Alpha, tex2); -#else - t->tex2((level->getLightColor(x, yl, z, 0) * 3 + 0xf000f0) / - 4); - t->color(br, br, br, - ((1 - dd * dd) * 0.3f + 0.5f) * rainLevel); - t->vertexUV(x - xa + 0.5, yy0, z - za + 0.5, 0 * s + uo, - yy0 * s / 4.0f + ra * s + vo); - t->vertexUV(x + xa + 0.5, yy0, z + za + 0.5, 1 * s + uo, - yy0 * s / 4.0f + ra * s + vo); - t->vertexUV(x + xa + 0.5, yy1, z + za + 0.5, 1 * s + uo, - yy1 * s / 4.0f + ra * s + vo); - t->vertexUV(x - xa + 0.5, yy1, z - za + 0.5, 0 * s + uo, - yy1 * s / 4.0f + ra * s + vo); -#endif - t->offset(0, 0, 0); - } - } + t->offset(0, 0, 0); } + } + t->end(); // single submit for all rain geometry + // sno time + mc->textures->bindTexture(&SNOW_LOCATION); + t->begin(); + for (int x = x0 - r; x <= x0 + r; x++) { + for (int z = z0 - r; z <= z0 + r; z++) { + int rainSlot = (z - z0 + 16) * 32 + (x - x0 + 16); + float xa = rainXa[rainSlot] * 0.5f; + float za = rainZa[rainSlot] * 0.5f; + + Biome* b = level->getBiome(x, z); + if (!b->hasRain() && !b->hasSnow()) continue; + + int floor = level->getTopRainBlock(x, z); + int yy0 = y0 - r; + int yy1 = y0 + r; + if (yy0 < floor) yy0 = floor; + if (yy1 < floor) yy1 = floor; + if (yy0 == yy1) continue; + + int yl = floor; + if (yl < yMin) yl = yMin; + + float temp = b->getTemperature(); + // only draw snow (not rain) in this pass + if (level->getBiomeSource()->scaleTemp(temp, floor) >= 0.15f) + continue; + + random->setSeed((x * x * 3121 + x * 45238971) ^ + (z * z * 418711 + z * 13761)); + + float ra = (((_tick) & 511) + a) / 512.0f; + float uo = random->nextFloat() + + time * 0.01f * (float)random->nextGaussian(); + float vo = random->nextFloat() + + time * (float)random->nextGaussian() * 0.001f; + + double xd = (x + 0.5f) - player->x; + double zd = (z + 0.5f) - player->z; + float dd = (float)sqrt(xd * xd + zd * zd) / r; + + float br = 1.0f; + float s = 1.0f; + t->offset(-xo, -yo, -zo); +#ifdef __PSVITA__ + float Alpha = ((1 - dd * dd) * 0.3f + 0.5f) * rainLevel; + int tex2 = (level->getLightColor(x, yl, z, 0) * 3 + 0xf000f0) / 4; + t->tileRainQuad( + x - xa + 0.5, yy0, z - za + 0.5, 0 * s + uo, + yy0 * s / 4.0f + ra * s + vo, x + xa + 0.5, yy0, z + za + 0.5, + 1 * s + uo, yy0 * s / 4.0f + ra * s + vo, x + xa + 0.5, yy1, + z + za + 0.5, 1 * s + uo, yy1 * s / 4.0f + ra * s + vo, + x - xa + 0.5, yy1, z - za + 0.5, 0 * s + uo, + yy1 * s / 4.0f + ra * s + vo, br, br, br, Alpha, br, br, br, + Alpha, tex2); +#else + t->tex2((level->getLightColor(x, yl, z, 0) * 3 + 0xf000f0) / 4); + t->color(br, br, br, ((1 - dd * dd) * 0.3f + 0.5f) * rainLevel); + t->vertexUV(x - xa + 0.5, yy0, z - za + 0.5, 0 * s + uo, + yy0 * s / 4.0f + ra * s + vo); + t->vertexUV(x + xa + 0.5, yy0, z + za + 0.5, 1 * s + uo, + yy0 * s / 4.0f + ra * s + vo); + t->vertexUV(x + xa + 0.5, yy1, z + za + 0.5, 1 * s + uo, + yy1 * s / 4.0f + ra * s + vo); + t->vertexUV(x - xa + 0.5, yy1, z - za + 0.5, 0 * s + uo, + yy1 * s / 4.0f + ra * s + vo); +#endif + t->offset(0, 0, 0); + } + } + t->end(); // single submit for all snow geometry - if (mode >= 0) t->end(); glEnable(GL_CULL_FACE); glDisable(GL_BLEND); glAlphaFunc(GL_GREATER, 0.1f); @@ -1845,6 +1897,35 @@ void GameRenderer::setupGuiScreen(int forceScale /*=-1*/) { // 4jcraft: use actual framebuffer dimensions instead of mc->width/height // to ensure GUI scales correctly after a window resize. ScreenSizeCalculator ssc(mc->options, fbw, fbh, forceScale); + + // 4jcraft: Java GUI screens still assume a clean 2D fixed-function style + // state. + RenderManager.StateSetFaceCull(false); + glDisable(GL_LIGHTING); + glDisable(GL_FOG); + glColor4f(1, 1, 1, 1); + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.1f); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glDepthMask(true); + + RenderManager.TextureBindVertex(-1); + + glClientActiveTexture(GL_TEXTURE1); + glActiveTexture(GL_TEXTURE1); + glDisable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + + glClientActiveTexture(GL_TEXTURE0); + glActiveTexture(GL_TEXTURE0); + glEnable(GL_TEXTURE_2D); + glMatrixMode(GL_TEXTURE); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glClear(GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); diff --git a/Minecraft.Client/Rendering/LevelRenderer.cpp b/Minecraft.Client/Rendering/LevelRenderer.cpp index 0d5e9afa7..160b945c1 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.cpp +++ b/Minecraft.Client/Rendering/LevelRenderer.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "../Platform/stdafx.h" #include "LevelRenderer.h" @@ -63,6 +64,7 @@ #include "../Level/MultiPlayerLevel.h" #include "../../Minecraft.World/Util/SoundTypes.h" #include "FrustumCuller.h" +#include "../Utils/FrameProfiler.h" // #define DISABLE_SPU_CODE @@ -89,6 +91,9 @@ ResourceLocation LevelRenderer::END_SKY_LOCATION = const unsigned int HALO_RING_RADIUS = 100; +uint64_t* LevelRenderer::globalChunkConnectivity = + nullptr; // bad placement do bettr juicey + #ifdef _LARGE_WORLDS Chunk LevelRenderer::permaChunk[MAX_CONCURRENT_CHUNK_REBUILDS]; C4JThread* LevelRenderer::rebuildThreads[MAX_CHUNK_REBUILD_THREADS]; @@ -187,6 +192,10 @@ LevelRenderer::LevelRenderer(Minecraft* mc, Textures* textures) { globalChunkFlags = new unsigned char[getGlobalChunkCount()]; memset(globalChunkFlags, 0, getGlobalChunkCount()); + globalChunkConnectivity = new uint64_t[getGlobalChunkCount()]; + memset(globalChunkConnectivity, 0xFF, + getGlobalChunkCount() * sizeof(uint64_t)); // 0xFF >> Fully open + starList = MemoryTracker::genLists(4); glPushMatrix(); @@ -401,8 +410,12 @@ void LevelRenderer::setLevel(int playerIndex, MultiPlayerLevel* level) { // tile entities in the world dissappear We should only do this when // actually exiting the game, so only when the primary player sets there // level to NULL - if (playerIndex == ProfileManager.GetPrimaryPad()) + if (playerIndex == ProfileManager.GetPrimaryPad()) { + EnterCriticalSection(&m_csRenderableTileEntities); renderableTileEntities.clear(); + m_renderableTileEntitiesPendingRemoval.clear(); + LeaveCriticalSection(&m_csRenderableTileEntities); + } } } @@ -632,35 +645,12 @@ void LevelRenderer::renderEntities(Vec3* cam, Culler* culler, float a) { // Don't render if it isn't in the same dimension as this player if (!isGlobalIndexInSameDimension(idx, level[playerIndex])) continue; - for (AUTO_VAR(it2, it->second.begin()); it2 != it->second.end(); - it2++) { + for (AUTO_VAR(it2, it->second.tiles.begin()); + it2 != it->second.tiles.end(); it2++) { TileEntityRenderDispatcher::instance->render(*it2, a); } } - // Now consider if any of these renderable tile entities have been flagged - // for removal, and if so, remove - for (AUTO_VAR(it, renderableTileEntities.begin()); - it != renderableTileEntities.end();) { - int idx = it->first; - - for (AUTO_VAR(it2, it->second.begin()); it2 != it->second.end();) { - // If it has been flagged for removal, remove - if ((*it2)->shouldRemoveForRender()) { - it2 = it->second.erase(it2); - } else { - it2++; - } - } - - // If there aren't any entities left for this key, then delete the key - if (it->second.size() == 0) { - it = renderableTileEntities.erase(it); - } else { - it++; - } - } - LeaveCriticalSection(&m_csRenderableTileEntities); mc->gameRenderer->turnOffLightLayer(a); // 4J - brought forward from 1.8.2 @@ -736,6 +726,8 @@ void LevelRenderer::resortChunks(int xc, int yc, int zc) { int LevelRenderer::render(std::shared_ptr player, int layer, double alpha, bool updateChunks) { + FRAME_PROFILE_SCOPE(Terrain); + int playerIndex = mc->player->GetXboxPad(); // 4J - added - if the number of players has changed, we need to rebuild @@ -775,10 +767,10 @@ int LevelRenderer::render(std::shared_ptr player, int layer, } Lighting::turnOff(); glColor4f(1, 1, 1, 1); - glColor4f(1, 1, 1, 1); + mc->gameRenderer->turnOnLightLayer(alpha); int count = renderChunks(0, (int)chunks[playerIndex].length, layer, alpha); - + mc->gameRenderer->turnOffLightLayer(alpha); return count; } @@ -796,39 +788,26 @@ int compare(const void* a, const void* b) { } #endif - int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) { - int playerIndex = mc->player->GetXboxPad(); // 4J added - -#if 1 - // 4J - cut down version, we're not using offsetted render lists, or a - // sorted chunk list, anymore - mc->gameRenderer->turnOnLightLayer( - alpha); // 4J - brought forward from 1.8.2 + int playerIndex = mc->player->GetXboxPad(); + if (chunks[playerIndex].data == NULL) return 0; + mc->gameRenderer->turnOnLightLayer(alpha); std::shared_ptr player = mc->cameraTargetPlayer; double xOff = player->xOld + (player->x - player->xOld) * alpha; double yOff = player->yOld + (player->y - player->yOld) * alpha; double zOff = player->zOld + (player->z - player->zOld) * alpha; glPushMatrix(); + glTranslatef((float)-xOff, (float)-yOff, (float)-zOff); -#ifdef __PSVITA__ - // AP - also set the camera position so we can work out if a chunk is fogged - // or not - RenderManager.SetCameraPosition((float)-xOff, (float)-yOff, (float)-zOff); -#endif - -#if defined __PS3__ && !defined DISABLE_SPU_CODE - // pre- calc'd on the SPU - int count = 0; +#ifdef __PS3__ waitForCull_SPU(); if (layer == 0) { count = g_cullDataIn[playerIndex].numToRender_layer0; RenderManager.CBuffCallMultiple( g_cullDataIn[playerIndex].listArray_layer0, count); - } else // layer == 1 - { + } else { // layer == 1 count = g_cullDataIn[playerIndex].numToRender_layer1; RenderManager.CBuffCallMultiple( g_cullDataIn[playerIndex].listArray_layer1, count); @@ -846,24 +825,64 @@ int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) { int count = 0; ClipChunk* pClipChunk = chunks[playerIndex].data; unsigned char emptyFlag = LevelRenderer::CHUNK_FLAG_EMPTY0 << layer; - for (int i = 0; i < chunks[playerIndex].length; i++, pClipChunk++) { - if (!pClipChunk->visible) - continue; // This will be set if the chunk isn't visible, or isn't - // compiled, or has both empty flags set - if (pClipChunk->globalIdx == -1) - continue; // Not sure if we should ever encounter this... TODO - // check - if ((globalChunkFlags[pClipChunk->globalIdx] & emptyFlag) == emptyFlag) - continue; // Check that this particular layer isn't empty + static thread_local std::vector sortList; + sortList.clear(); + if (sortList.capacity() < (size_t)chunks[playerIndex].length) { + sortList.reserve(chunks[playerIndex].length); + } + { + FRAME_PROFILE_SCOPE(ChunkCollect); + for (int i = 0; i < chunks[playerIndex].length; i++, pClipChunk++) { + if (!pClipChunk->visible) + continue; // This will be set if the chunk isn't visible, or + // isn't compiled, or has both empty flags set + if (pClipChunk->globalIdx == -1) + continue; // Not sure if we should ever encounter this... + // TODO check + if ((globalChunkFlags[pClipChunk->globalIdx] & emptyFlag) == + emptyFlag) + continue; - // List can be calculated directly from the chunk's global idex - int list = pClipChunk->globalIdx * 2 + layer; - list += chunkLists; - - if (RenderManager.CBuffCall(list, first)) { - first = false; + sortList.push_back(pClipChunk); } - count++; + // he sorts me till i + std::sort(sortList.begin(), sortList.end(), + [xOff, yOff, zOff, layer](ClipChunk* a, ClipChunk* b) { + float dxA = (float)((a->chunk->x + 8.0f) - xOff); + float dyA = (float)((a->chunk->y + 8.0f) - yOff); + float dzA = (float)((a->chunk->z + 8.0f) - zOff); + float distSqA = dxA * dxA + dyA * dyA + dzA * dzA; + + float dxB = (float)((b->chunk->x + 8.0f) - xOff); + float dyB = (float)((b->chunk->y + 8.0f) - yOff); + float dzB = (float)((b->chunk->z + 8.0f) - zOff); + float distSqB = dxB * dxB + dyB * dyB + dzB * dzB; + + if (layer == 0) + return distSqA < distSqB; // Opaque: Closest first + return distSqA > distSqB; // Transparent: Furthest + // first + }); + } + + { + FRAME_PROFILE_SCOPE(ChunkPlayback); + for (ClipChunk* chunk : sortList) { + int list = chunk->globalIdx * 2 + layer; + list += chunkLists; + + // 4jcraft: replaced glPushMatrix/glTranslatef/glPopMatrix per chunk + // no more full MVP upload per chunk, can also be bkwards compat + RenderManager.SetChunkOffset((float)chunk->chunk->x, + (float)chunk->chunk->y, + (float)chunk->chunk->z); + + if (RenderManager.CBuffCall(list, first)) { + first = false; + } + count++; + } + RenderManager.SetChunkOffset(0.f, 0.f, 0.f); } #ifdef __PSVITA__ @@ -875,25 +894,29 @@ int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) { first = true; pClipChunk = chunks[playerIndex].data; emptyFlag = LevelRenderer::CHUNK_FLAG_EMPTY0 << layer; - for (int i = 0; i < chunks[playerIndex].length; i++, pClipChunk++) { - if (!pClipChunk->visible) - continue; // This will be set if the chunk isn't visible, or isn't - // compiled, or has both empty flags set - if (pClipChunk->globalIdx == -1) - continue; // Not sure if we should ever encounter this... TODO - // check - if ((globalChunkFlags[pClipChunk->globalIdx] & emptyFlag) == emptyFlag) - continue; // Check that this particular layer isn't empty - if (!(globalChunkFlags[pClipChunk->globalIdx] & - LevelRenderer::CHUNK_FLAG_CUT_OUT)) - continue; // Does this chunk contain any cut out geometry + { + FRAME_PROFILE_SCOPE(ChunkPlayback); + for (int i = 0; i < chunks[playerIndex].length; i++, pClipChunk++) { + if (!pClipChunk->visible) + continue; // This will be set if the chunk isn't visible, or + // isn't compiled, or has both empty flags set + if (pClipChunk->globalIdx == -1) + continue; // Not sure if we should ever encounter this... TODO + // check + if ((globalChunkFlags[pClipChunk->globalIdx] & emptyFlag) == + emptyFlag) + continue; // Check that this particular layer isn't empty + if (!(globalChunkFlags[pClipChunk->globalIdx] & + LevelRenderer::CHUNK_FLAG_CUT_OUT)) + continue; // Does this chunk contain any cut out geometry - // List can be calculated directly from the chunk's global idex - int list = pClipChunk->globalIdx * 2 + layer; - list += chunkLists; + // List can be calculated directly from the chunk's global idex + int list = pClipChunk->globalIdx * 2 + layer; + list += chunkLists; - if (RenderManager.CBuffCallCutOut(list, first)) { - first = false; + if (RenderManager.CBuffCallCutOut(list, first)) { + first = false; + } } } RenderManager.StateSetForceLOD(-1); // AP - back to normal mipmapping @@ -902,69 +925,7 @@ int LevelRenderer::renderChunks(int from, int to, int layer, double alpha) { #endif // __PS3__ glPopMatrix(); - mc->gameRenderer->turnOffLightLayer( - alpha); // 4J - brought forward from 1.8.2 - -#else - _renderChunks.clear(); - // int p = 0; - int count = 0; - for (int i = from; i < to; i++) { - if (layer == 0) { - totalChunks++; - if (sortedChunks[playerIndex]->at(i)->emptyFlagSet(layer)) - emptyChunks++; - else if (!sortedChunks[playerIndex]->at(i)->visible) - offscreenChunks++; - else - renderedChunks++; - } - - // if (!sortedChunks[i].empty[layer] && - // sortedChunks[i].visible && - // (sortedChunks[i].occlusion_visible)) { - if (!(sortedChunks[playerIndex]->at(i)->emptyFlagSet(layer) && - sortedChunks[playerIndex]->at(i)->visible)) { - int list = sortedChunks[playerIndex]->at(i)->getList(layer); - if (list >= 0) { - _renderChunks.push_back(sortedChunks[playerIndex]->at(i)); - count++; - } - } - } - - std::shared_ptr player = mc->cameraTargetPlayer; - double xOff = player->xOld + (player->x - player->xOld) * alpha; - double yOff = player->yOld + (player->y - player->yOld) * alpha; - double zOff = player->zOld + (player->z - player->zOld) * alpha; - - int lists = 0; - for (int l = 0; l < RENDERLISTS_LENGTH; l++) { - renderLists[l].clear(); - } - - AUTO_VAR(itEnd, _renderChunks.end()); - for (AUTO_VAR(it, _renderChunks.begin()); it != itEnd; it++) { - Chunk* chunk = *it; //_renderChunks[i]; - - int list = -1; - for (int l = 0; l < lists; l++) { - if (renderLists[l].isAt(chunk->xRender, chunk->yRender, - chunk->zRender)) { - list = l; - } - } - if (list < 0) { - list = lists++; - renderLists[list].init(chunk->xRender, chunk->yRender, - chunk->zRender, xOff, yOff, zOff); - } - - renderLists[list].add(chunk->getList(layer)); - } - - renderSameAsLast(layer, alpha); -#endif + mc->gameRenderer->turnOffLightLayer(alpha); return count; } @@ -1916,7 +1877,40 @@ void LevelRenderer::renderAdvancedClouds(float alpha) { bool LevelRenderer::updateDirtyChunks() { #ifdef _LARGE_WORLDS - std::list > nearestClipChunks; + struct NearestClipChunkSet { + std::array, MAX_CONCURRENT_CHUNK_REBUILDS> + items; + int count = 0; + + bool empty() const noexcept { return count == 0; } + int size() const noexcept { return count; } + + bool wouldAccept(int distSqWeighted) const noexcept { + return (count < MAX_CONCURRENT_CHUNK_REBUILDS) || + (distSqWeighted < items[count - 1].second); + } + + void insert(ClipChunk* chunk, int distSqWeighted) noexcept { + int pos = 0; + while ((pos < count) && (items[pos].second <= distSqWeighted)) { + ++pos; + } + + if ((count == MAX_CONCURRENT_CHUNK_REBUILDS) && + (pos >= MAX_CONCURRENT_CHUNK_REBUILDS)) { + return; + } + + const int newCount = (count < MAX_CONCURRENT_CHUNK_REBUILDS) + ? (count + 1) + : MAX_CONCURRENT_CHUNK_REBUILDS; + for (int i = newCount - 1; i > pos; --i) { + items[i] = items[i - 1]; + } + items[pos] = std::pair(chunk, distSqWeighted); + count = newCount; + } + } nearestClipChunks; #endif ClipChunk* nearChunk = NULL; // Nearest chunk that is dirty @@ -1925,279 +1919,370 @@ bool LevelRenderer::updateDirtyChunks() { // Set a flag if we should only rebuild existing chunks, not create anything // new - unsigned int memAlloc = RenderManager.CBuffSize(-1); - /* - static int throttle = 0; - if( ( throttle % 100 ) == 0 ) { - app.DebugPrintf("CBuffSize: %d\n",memAlloc/(1024*1024)); - } - throttle++; - */ - PIXAddNamedCounter(((float)memAlloc) / (1024.0f * 1024.0f), - "Command buffer allocations"); - bool onlyRebuild = (memAlloc >= MAX_COMMANDBUFFER_ALLOCATIONS); - EnterCriticalSection(&m_csDirtyChunks); + FRAME_PROFILE_SCOPE(ChunkDirtyScan); - // Move any dirty chunks stored in the lock free stack into global flags - int index = 0; - - do { - // See comment on dirtyChunksLockFreeStack.Push() regarding details of - // this casting/subtracting -2. - index = (size_t)dirtyChunksLockFreeStack.Pop(); -#ifdef _CRITICAL_CHUNKS - int oldIndex = index; - index &= 0x0fffffff; // remove the top bit that marked the chunk as - // non-critical -#endif - if (index == 1) - dirtyChunkPresent = - true; // 1 is a special value passed to let this thread know - // that a chunk which isn't on this stack has been set to - // dirty - else if (index > 1) { - int i2 = index - 2; - if (i2 >= DIMENSION_OFFSETS[2]) { - i2 -= DIMENSION_OFFSETS[2]; - int y2 = i2 & (CHUNK_Y_COUNT - 1); - i2 /= CHUNK_Y_COUNT; - int z2 = i2 / MAX_LEVEL_RENDER_SIZE[2]; - int x2 = i2 - z2 * MAX_LEVEL_RENDER_SIZE[2]; - x2 -= MAX_LEVEL_RENDER_SIZE[2] / 2; - z2 -= MAX_LEVEL_RENDER_SIZE[2] / 2; - } - setGlobalChunkFlag(index - 2, CHUNK_FLAG_DIRTY); - -#ifdef _CRITICAL_CHUNKS - if (!(oldIndex & 0x10000000)) // was this chunk not marked as - // non-critical. Ugh double negatives - { - setGlobalChunkFlag(index - 2, CHUNK_FLAG_CRITICAL); - } -#endif - - dirtyChunkPresent = true; + unsigned int memAlloc = RenderManager.CBuffSize(-1); + /* + static int throttle = 0; + if( ( throttle % 100 ) == 0 ) + { + app.DebugPrintf("CBuffSize: %d\n",memAlloc/(1024*1024)); } - } while (index); + throttle++; + */ + PIXAddNamedCounter(((float)memAlloc) / (1024.0f * 1024.0f), + "Command buffer allocations"); + bool onlyRebuild = (memAlloc >= MAX_COMMANDBUFFER_ALLOCATIONS); + EnterCriticalSection(&m_csDirtyChunks); - // Only bother searching round all the chunks if we have some dirty chunk(s) - if (dirtyChunkPresent) { - lastDirtyChunkFound = System::currentTimeMillis(); - PIXBeginNamedEvent(0, "Finding nearest chunk\n"); + // Move any dirty chunks stored in the lock free stack into global flags + int index = 0; + + do { + // See comment on dirtyChunksLockFreeStack.Push() regarding details + // of this casting/subtracting -2. + index = (size_t)dirtyChunksLockFreeStack.Pop(); +#ifdef _CRITICAL_CHUNKS + int oldIndex = index; + index &= 0x0fffffff; // remove the top bit that marked the chunk as + // non-critical +#endif + if (index == 1) + dirtyChunkPresent = + true; // 1 is a special value passed to let this thread + // know that a chunk which isn't on this stack has + // been set to dirty + else if (index > 1) { + int i2 = index - 2; + if (i2 >= DIMENSION_OFFSETS[2]) { + i2 -= DIMENSION_OFFSETS[2]; + int y2 = i2 & (CHUNK_Y_COUNT - 1); + i2 /= CHUNK_Y_COUNT; + int z2 = i2 / MAX_LEVEL_RENDER_SIZE[2]; + int x2 = i2 - z2 * MAX_LEVEL_RENDER_SIZE[2]; + x2 -= MAX_LEVEL_RENDER_SIZE[2] / 2; + z2 -= MAX_LEVEL_RENDER_SIZE[2] / 2; + } + setGlobalChunkFlag(index - 2, CHUNK_FLAG_DIRTY); + +#ifdef _CRITICAL_CHUNKS + if (!(oldIndex & + 0x10000000)) // was this chunk not marked as + // non-critical. Ugh double negatives + { + setGlobalChunkFlag(index - 2, CHUNK_FLAG_CRITICAL); + } +#endif + + dirtyChunkPresent = true; + } + } while (index); + + // Only bother searching round all the chunks if we have some dirty + // chunk(s) + if (dirtyChunkPresent) { + lastDirtyChunkFound = System::currentTimeMillis(); + PIXBeginNamedEvent(0, "Finding nearest chunk\n"); #if defined __PS3__ && !defined DISABLE_SPU_CODE - // find the nearest chunk with a spu task, copy all the data over here - // for uploading to SPU - g_findNearestChunkDataIn.numGlobalChunks = getGlobalChunkCount(); - g_findNearestChunkDataIn.pGlobalChunkFlags = globalChunkFlags; - g_findNearestChunkDataIn.onlyRebuild = onlyRebuild; - g_findNearestChunkDataIn.lowerOffset = - (int)&((LevelChunk*)0) - ->lowerBlocks; // dodgy bit of class structure poking, as we - // don't want to try and get the whole of - // LevelChunk copmpiling on SPU - g_findNearestChunkDataIn.upperOffset = - (int)&((LevelChunk*)0)->upperBlocks; - g_findNearestChunkDataIn.xChunks = xChunks; - g_findNearestChunkDataIn.yChunks = yChunks; - g_findNearestChunkDataIn.zChunks = zChunks; + // find the nearest chunk with a spu task, copy all the data over + // here for uploading to SPU + g_findNearestChunkDataIn.numGlobalChunks = getGlobalChunkCount(); + g_findNearestChunkDataIn.pGlobalChunkFlags = globalChunkFlags; + g_findNearestChunkDataIn.onlyRebuild = onlyRebuild; + g_findNearestChunkDataIn.lowerOffset = + (int)&((LevelChunk*)0) + ->lowerBlocks; // dodgy bit of class structure poking, as + // we don't want to try and get the whole of + // LevelChunk copmpiling on SPU + g_findNearestChunkDataIn.upperOffset = + (int)&((LevelChunk*)0)->upperBlocks; + g_findNearestChunkDataIn.xChunks = xChunks; + g_findNearestChunkDataIn.yChunks = yChunks; + g_findNearestChunkDataIn.zChunks = zChunks; - for (int i = 0; i < 4; i++) { - g_findNearestChunkDataIn.chunks[i] = - (LevelRenderer_FindNearestChunk_DataIn::ClipChunk*)chunks[i] - .data; - g_findNearestChunkDataIn.chunkLengths[i] = chunks[i].length; - g_findNearestChunkDataIn.level[i] = level[i]; - g_findNearestChunkDataIn.playerData[i].bValid = - mc->localplayers[i] != NULL; - if (mc->localplayers[i] != NULL) { - g_findNearestChunkDataIn.playerData[i].x = - mc->localplayers[i]->x; - g_findNearestChunkDataIn.playerData[i].y = - mc->localplayers[i]->y; - g_findNearestChunkDataIn.playerData[i].z = - mc->localplayers[i]->z; + for (int i = 0; i < 4; i++) { + g_findNearestChunkDataIn.chunks[i] = + (LevelRenderer_FindNearestChunk_DataIn::ClipChunk*)chunks[i] + .data; + g_findNearestChunkDataIn.chunkLengths[i] = chunks[i].length; + g_findNearestChunkDataIn.level[i] = level[i]; + g_findNearestChunkDataIn.playerData[i].bValid = + mc->localplayers[i] != NULL; + if (mc->localplayers[i] != NULL) { + g_findNearestChunkDataIn.playerData[i].x = + mc->localplayers[i]->x; + g_findNearestChunkDataIn.playerData[i].y = + mc->localplayers[i]->y; + g_findNearestChunkDataIn.playerData[i].z = + mc->localplayers[i]->z; + } + if (level[i] != NULL) { + g_findNearestChunkDataIn.multiplayerChunkCache[i].XZOFFSET = + ((MultiPlayerChunkCache*)(level[i]->chunkSource)) + ->XZOFFSET; + g_findNearestChunkDataIn.multiplayerChunkCache[i].XZSIZE = + ((MultiPlayerChunkCache*)(level[i]->chunkSource)) + ->XZSIZE; + g_findNearestChunkDataIn.multiplayerChunkCache[i].cache = + (void**)((MultiPlayerChunkCache*)(level[i] + ->chunkSource)) + ->cache; + } } - if (level[i] != NULL) { - g_findNearestChunkDataIn.multiplayerChunkCache[i].XZOFFSET = - ((MultiPlayerChunkCache*)(level[i]->chunkSource))->XZOFFSET; - g_findNearestChunkDataIn.multiplayerChunkCache[i].XZSIZE = - ((MultiPlayerChunkCache*)(level[i]->chunkSource))->XZSIZE; - g_findNearestChunkDataIn.multiplayerChunkCache[i].cache = - (void**)((MultiPlayerChunkCache*)(level[i]->chunkSource)) - ->cache; - } - } - // assert(sizeof(LevelRenderer_FindNearestChunk_DataIn::Chunk) - // == sizeof(Chunk)); - C4JSpursJob_LevelRenderer_FindNearestChunk findJob( - &g_findNearestChunkDataIn); - m_jobPort_FindNearestChunk->submitJob(&findJob); - m_jobPort_FindNearestChunk->waitForCompletion(); - nearChunk = (ClipChunk*)g_findNearestChunkDataIn.nearChunk; - veryNearCount = g_findNearestChunkDataIn.veryNearCount; + // assert(sizeof(LevelRenderer_FindNearestChunk_DataIn::Chunk) + // == sizeof(Chunk)); + C4JSpursJob_LevelRenderer_FindNearestChunk findJob( + &g_findNearestChunkDataIn); + m_jobPort_FindNearestChunk->submitJob(&findJob); + m_jobPort_FindNearestChunk->waitForCompletion(); + nearChunk = (ClipChunk*)g_findNearestChunkDataIn.nearChunk; + veryNearCount = g_findNearestChunkDataIn.veryNearCount; #else // __PS3__ + // Find nearest chunk that is dirty + for (int p = 0; p < XUSER_MAX_COUNT; p++) { + // It's possible that the localplayers member can be set to NULL + // on the main thread when a player chooses to exit the game So + // take a reference to the player object now. As it is a + // shared_ptr it should live as long as we need it + std::shared_ptr player = mc->localplayers[p]; + if (player == NULL) continue; + if (chunks[p].data == NULL) continue; + if (level[p] == NULL) continue; + if (chunks[p].length != xChunks * zChunks * CHUNK_Y_COUNT) + continue; + int px = (int)player->x; + int py = (int)player->y; + int pz = (int)player->z; + + // app.DebugPrintf("!! %d %d %d, %d %d %d + //{%d,%d} + //",px,py,pz,stackChunkDirty,nonStackChunkDirty,onlyRebuild, + // xChunks, zChunks); + + int considered = 0; + int wouldBeNearButEmpty = 0; + for (int x = 0; x < xChunks; x++) { + for (int z = 0; z < zChunks; z++) { + for (int y = 0; y < CHUNK_Y_COUNT; y++) { + ClipChunk* pClipChunk = + &chunks[p][(z * yChunks + y) * xChunks + x]; + // Get distance to this chunk - deliberately not + // calling the chunk's method of doing this to avoid + // overheads (passing entitie, type conversion etc.) + // that this involves + int xd = pClipChunk->xm - px; + int yd = pClipChunk->ym - py; + int zd = pClipChunk->zm - pz; + int distSq = xd * xd + yd * yd + zd * zd; + int distSqWeighted = + xd * xd + yd * yd * 4 + + zd * zd; // Weighting against y to prioritise + // things in same x/z plane as player + // first + + if (globalChunkFlags[pClipChunk->globalIdx] & + CHUNK_FLAG_DIRTY) { + if ((!onlyRebuild) || + globalChunkFlags[pClipChunk->globalIdx] & + CHUNK_FLAG_COMPILED || + (distSq < + 20 * 20)) // Always rebuild really near + // things or else building (say) + // at tower up into empty blocks + // when we are low on memory + // will not create render data + { + considered++; + // Is this chunk nearer than our nearest? #ifdef _LARGE_WORLDS - int maxNearestChunks = MAX_CONCURRENT_CHUNK_REBUILDS; - // 4J Stu - On XboxOne we should cut this down if in a constrained state - // so the saving threads get more time -#endif - // Find nearest chunk that is dirty - for (int p = 0; p < XUSER_MAX_COUNT; p++) { - // It's possible that the localplayers member can be set to NULL on - // the main thread when a player chooses to exit the game So take a - // reference to the player object now. As it is a shared_ptr it - // should live as long as we need it - std::shared_ptr player = mc->localplayers[p]; - if (player == NULL) continue; - if (chunks[p].data == NULL) continue; - if (level[p] == NULL) continue; - if (chunks[p].length != xChunks * zChunks * CHUNK_Y_COUNT) continue; - int px = (int)player->x; - int py = (int)player->y; - int pz = (int)player->z; - - // app.DebugPrintf("!! %d %d %d, %d %d %d {%d,%d} - //",px,py,pz,stackChunkDirty,nonStackChunkDirty,onlyRebuild, - // xChunks, zChunks); - - int considered = 0; - int wouldBeNearButEmpty = 0; - for (int x = 0; x < xChunks; x++) { - for (int z = 0; z < zChunks; z++) { - for (int y = 0; y < CHUNK_Y_COUNT; y++) { - ClipChunk* pClipChunk = - &chunks[p][(z * yChunks + y) * xChunks + x]; - // Get distance to this chunk - deliberately not calling - // the chunk's method of doing this to avoid overheads - // (passing entitie, type conversion etc.) that this - // involves - int xd = pClipChunk->xm - px; - int yd = pClipChunk->ym - py; - int zd = pClipChunk->zm - pz; - int distSq = xd * xd + yd * yd + zd * zd; - int distSqWeighted = - xd * xd + yd * yd * 4 + - zd * - zd; // Weighting against y to prioritise things - // in same x/z plane as player first - - if (globalChunkFlags[pClipChunk->globalIdx] & - CHUNK_FLAG_DIRTY) { - if ((!onlyRebuild) || - globalChunkFlags[pClipChunk->globalIdx] & - CHUNK_FLAG_COMPILED || - (distSq < - 20 * 20)) // Always rebuild really near things - // or else building (say) at tower - // up into empty blocks when we are - // low on memory will not create - // render data - { - considered++; - // Is this chunk nearer than our nearest? -#ifdef _LARGE_WORLDS - bool isNearer = nearestClipChunks.empty(); - AUTO_VAR(itNearest, nearestClipChunks.begin()); - for (; itNearest != nearestClipChunks.end(); - ++itNearest) { - isNearer = - distSqWeighted < itNearest->second; - if (isNearer) break; - } - isNearer = - isNearer || (nearestClipChunks.size() < - maxNearestChunks); + bool isNearer = + nearestClipChunks.wouldAccept( + distSqWeighted); #else - bool isNearer = distSqWeighted < minDistSq; + bool isNearer = distSqWeighted < minDistSq; #endif #ifdef _CRITICAL_CHUNKS - // AP - this will make sure that if a deferred - // grouping has started, only critical chunks go - // into that grouping, even if a non-critical - // chunk is closer. - if ((!veryNearCount && isNearer) || - (distSq < 20 * 20 && - (globalChunkFlags[pClipChunk->globalIdx] & - CHUNK_FLAG_CRITICAL))) + // AP - this will make sure that if a + // deferred grouping has started, only + // critical chunks go into that grouping, + // even if a non-critical chunk is closer. + if ((!veryNearCount && isNearer) || + (distSq < 20 * 20 && + (globalChunkFlags[pClipChunk + ->globalIdx] & + CHUNK_FLAG_CRITICAL))) #else - if (isNearer) + if (isNearer) #endif - { - // At this point we've got a chunk that we - // would like to consider for rendering, at - // least based on its proximity to the - // player(s). Its *quite* quick to generate - // empty render data for render chunks, but - // if we let the rebuilding do that then the - // after rebuilding we will have to start - // searching for the next nearest chunk from - // scratch again. Instead, its better to - // detect empty chunks at this stage, flag - // them up as not dirty (and empty), and - // carry on. The levelchunk's - // isRenderChunkEmpty method can be quite - // optimal as it can make use of the chunk's - // data compression to detect emptiness - // without actually testing as many data - // items as uncompressed data would. - Chunk* chunk = pClipChunk->chunk; - LevelChunk* lc = level[p]->getChunkAt( - chunk->x, chunk->z); - if (!lc->isRenderChunkEmpty(y * 16)) { - nearChunk = pClipChunk; - minDistSq = distSqWeighted; + { + // At this point we've got a chunk that + // we would like to consider for + // rendering, at least based on its + // proximity to the player(s). Its + // *quite* quick to generate empty + // render data for render chunks, but if + // we let the rebuilding do that then + // the after rebuilding we will have to + // start searching for the next nearest + // chunk from scratch again. Instead, + // its better to detect empty chunks at + // this stage, flag them up as not dirty + // (and empty), and carry on. The + // levelchunk's isRenderChunkEmpty + // method can be quite optimal as it can + // make use of the chunk's data + // compression to detect emptiness + // without actually testing as many data + // items as uncompressed data would. + Chunk* chunk = pClipChunk->chunk; + LevelChunk* lc = level[p]->getChunkAt( + chunk->x, chunk->z); + if (!lc->isRenderChunkEmpty(y * 16)) { + nearChunk = pClipChunk; + minDistSq = distSqWeighted; #ifdef _LARGE_WORLDS - nearestClipChunks.insert( - itNearest, - std::pair( - nearChunk, minDistSq)); - if (nearestClipChunks.size() > - maxNearestChunks) { - nearestClipChunks.pop_back(); + nearestClipChunks.insert(nearChunk, + minDistSq); +#endif + } else { + chunk->clearDirty(); + globalChunkFlags[pClipChunk + ->globalIdx] |= + CHUNK_FLAG_EMPTYBOTH; + wouldBeNearButEmpty++; } -#endif - } else { - chunk->clearDirty(); - globalChunkFlags[pClipChunk - ->globalIdx] |= - CHUNK_FLAG_EMPTYBOTH; - wouldBeNearButEmpty++; } - } #ifdef _CRITICAL_CHUNKS - // AP - is the chunk near and also critical - if (distSq < 20 * 20 && - ((globalChunkFlags[pClipChunk->globalIdx] & - CHUNK_FLAG_CRITICAL))) + // AP - is the chunk near and also critical + if (distSq < 20 * 20 && + ((globalChunkFlags[pClipChunk + ->globalIdx] & + CHUNK_FLAG_CRITICAL))) #else - if (distSq < 20 * 20) + if (distSq < 20 * 20) #endif - { - veryNearCount++; + { + veryNearCount++; + } } } } } } + // app.DebugPrintf("[%d,%d,%d]\n",nearestClipChunks.empty(),considered,wouldBeNearButEmpty); } - // app.DebugPrintf("[%d,%d,%d]\n",nearestClipChunks.empty(),considered,wouldBeNearButEmpty); - } #endif // __PS3__ - PIXEndNamedEvent(); + PIXEndNamedEvent(); + } } Chunk* chunk = NULL; #ifdef _LARGE_WORLDS if (!nearestClipChunks.empty()) { int index = 0; - for (AUTO_VAR(it, nearestClipChunks.begin()); - it != nearestClipChunks.end(); ++it) { - chunk = it->first->chunk; + { + FRAME_PROFILE_SCOPE(ChunkRebuildSchedule); + for (int i = 0; i < nearestClipChunks.size(); ++i) { + chunk = nearestClipChunks.items[i].first->chunk; + // If this chunk is very near, then move the renderer into a + // deferred mode. This won't commit any command buffers for + // rendering until we call CBuffDeferredModeEnd(), allowing us + // to group any near changes into an atomic unit. This is + // essential so we don't temporarily create any holes in the + // environment whilst updating one chunk and not the neighbours. + // The "ver near" aspect of this is just a cosmetic nicety - + // exactly the same thing would happen further away, but we just + // don't care about it so much from terms of visual impact. + if (veryNearCount > 0) { + RenderManager.CBuffDeferredModeStart(); + } + // Build this chunk & return false to continue processing + chunk->clearDirty(); + // Take a copy of the details that are required for chunk + // rebuilding, and rebuild That instead of the original chunk + // data. This is done within the m_csDirtyChunks critical + // section, which means that any chunks can't be repositioned + // whilst we are doing this copy. The copy will then be + // guaranteed to be consistent whilst rebuilding takes place + // outside of that critical section. + permaChunk[index].makeCopyForRebuild(chunk); + ++index; + } + LeaveCriticalSection(&m_csDirtyChunks); + + --index; // Bring it back into 0 counted range + + for (int i = MAX_CHUNK_REBUILD_THREADS - 1; i >= 0; --i) { + // Set the events that won't run + if ((i + 1) > index) + s_rebuildCompleteEvents->Set(i); + else + break; + } + } + + for (; index >= 0; --index) { + bool bAtomic = false; + if ((veryNearCount > 0)) + bAtomic = true; // MGH - if veryNearCount, then we're trying + // to rebuild atomically, so do it all on the + // main thread + + if (bAtomic || (index == 0)) { + // PIXBeginNamedEvent(0,"Rebuilding near chunk %d %d + // %d",chunk->x, chunk->y, chunk->z); static int64_t + // totalTime = + // 0; static int64_t countTime = 0; + // int64_t startTime = System::currentTimeMillis(); + + // app.DebugPrintf("Rebuilding permaChunk %d\n", index); + + { + FRAME_PROFILE_SCOPE(ChunkRebuildBody); + permaChunk[index].rebuild(); + } + + if (index != 0) { + FRAME_PROFILE_SCOPE(ChunkRebuildSchedule); + s_rebuildCompleteEvents->Set( + index - 1); // MGH - this rebuild happening on the main + // thread instead, mark the thread it + // should have been running on as complete + } + + // int64_t endTime = System::currentTimeMillis(); + // totalTime += (endTime - startTime); + // countTime++; + // printf("%d : %f\n", countTime, (float)totalTime + /// (float)countTime); PIXEndNamedEvent(); + } + // 4J Stu - Ignore this path when in constrained mode on Xbox One + else { + // Activate thread to rebuild this chunk + FRAME_PROFILE_SCOPE(ChunkRebuildSchedule); + s_activationEventA[index - 1]->Set(); + } + } + + // Wait for the other threads to be done as well + { + FRAME_PROFILE_SCOPE(ChunkRebuildSchedule); + s_rebuildCompleteEvents->WaitForAll(INFINITE); + } + } +#else + if (nearChunk) { + chunk = nearChunk->chunk; + PIXBeginNamedEvent(0, "Rebuilding near chunk %d %d %d", chunk->x, + chunk->y, chunk->z); + static Chunk permaChunk; + { + FRAME_PROFILE_SCOPE(ChunkRebuildSchedule); // If this chunk is very near, then move the renderer into a // deferred mode. This won't commit any command buffers for // rendering until we call CBuffDeferredModeEnd(), allowing us to @@ -2218,92 +2303,16 @@ bool LevelRenderer::updateDirtyChunks() { // means that any chunks can't be repositioned whilst we are doing // this copy. The copy will then be guaranteed to be consistent // whilst rebuilding takes place outside of that critical section. - permaChunk[index].makeCopyForRebuild(chunk); - ++index; + permaChunk.makeCopyForRebuild(chunk); + LeaveCriticalSection(&m_csDirtyChunks); } - LeaveCriticalSection(&m_csDirtyChunks); - - --index; // Bring it back into 0 counted range - - for (int i = MAX_CHUNK_REBUILD_THREADS - 1; i >= 0; --i) { - // Set the events that won't run - if ((i + 1) > index) - s_rebuildCompleteEvents->Set(i); - else - break; - } - - for (; index >= 0; --index) { - bool bAtomic = false; - if ((veryNearCount > 0)) - bAtomic = true; // MGH - if veryNearCount, then we're trying - // to rebuild atomically, so do it all on the - // main thread - - if (bAtomic || (index == 0)) { - // PIXBeginNamedEvent(0,"Rebuilding near chunk %d %d - // %d",chunk->x, chunk->y, chunk->z); static int64_t - // totalTime = - // 0; static int64_t countTime = 0; - // int64_t startTime = System::currentTimeMillis(); - - // app.DebugPrintf("Rebuilding permaChunk %d\n", index); - - permaChunk[index].rebuild(); - - if (index != 0) - s_rebuildCompleteEvents->Set( - index - 1); // MGH - this rebuild happening on the main - // thread instead, mark the thread it - // should have been running on as complete - - // int64_t endTime = System::currentTimeMillis(); - // totalTime += (endTime - startTime); - // countTime++; - // printf("%d : %f\n", countTime, (float)totalTime - /// (float)countTime); PIXEndNamedEvent(); - } - // 4J Stu - Ignore this path when in constrained mode on Xbox One - else { - // Activate thread to rebuild this chunk - s_activationEventA[index - 1]->Set(); - } - } - - // Wait for the other threads to be done as well - s_rebuildCompleteEvents->WaitForAll(INFINITE); - } -#else - if (nearChunk) { - chunk = nearChunk->chunk; - PIXBeginNamedEvent(0, "Rebuilding near chunk %d %d %d", chunk->x, - chunk->y, chunk->z); - // If this chunk is very near, then move the renderer into a deferred - // mode. This won't commit any command buffers for rendering until we - // call CBuffDeferredModeEnd(), allowing us to group any near changes - // into an atomic unit. This is essential so we don't temporarily create - // any holes in the environment whilst updating one chunk and not the - // neighbours. The "ver near" aspect of this is just a cosmetic nicety - - // exactly the same thing would happen further away, but we just don't - // care about it so much from terms of visual impact. - if (veryNearCount > 0) { - RenderManager.CBuffDeferredModeStart(); - } - // Build this chunk & return false to continue processing - chunk->clearDirty(); - // Take a copy of the details that are required for chunk rebuilding, - // and rebuild That instead of the original chunk data. This is done - // within the m_csDirtyChunks critical section, which means that any - // chunks can't be repositioned whilst we are doing this copy. The copy - // will then be guaranteed to be consistent whilst rebuilding takes - // place outside of that critical section. - static Chunk permaChunk; - permaChunk.makeCopyForRebuild(chunk); - LeaveCriticalSection(&m_csDirtyChunks); // static int64_t totalTime = 0; // static int64_t countTime = 0; // int64_t startTime = System::currentTimeMillis(); - permaChunk.rebuild(); + { + FRAME_PROFILE_SCOPE(ChunkRebuildBody); + permaChunk.rebuild(); + } // int64_t endTime = System::currentTimeMillis(); // totalTime += (endTime - startTime); // countTime++; @@ -2456,22 +2465,27 @@ void LevelRenderer::renderDestroyAnimation(Tesselator* t, glPopMatrix(); } } - void LevelRenderer::renderHitOutline(std::shared_ptr player, HitResult* h, int mode, float a) { if (mode == 0 && h->type == HitResult::TILE) { int iPad = mc->player->GetXboxPad(); // 4J added + const float ss = 0.002f; + // 4J-PB - If Display HUD is false, don't render the hit outline if (app.GetGameSettings(iPad, eGameSetting_DisplayHUD) == 0) return; - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glColor4f(0, 0, 0, 0.4f); - glLineWidth(2.0f); + RenderManager.StateSetLightingEnable(false); glDisable(GL_TEXTURE_2D); - glDepthMask(false); - float ss = 0.002f; + + // draw hit outline + RenderManager.StateSetColour(0.0f, 0.0f, 0.0f, 0.4f); + RenderManager.StateSetLineWidth(1.0f); + + // hack + glDepthFunc(GL_LEQUAL); + glEnable(GL_POLYGON_OFFSET_LINE); + glPolygonOffset(-2.0f, -2.0f); + int tileId = level[iPad]->getTile(h->x, h->y, h->z); if (tileId > 0) { @@ -2487,41 +2501,63 @@ void LevelRenderer::renderHitOutline(std::shared_ptr player, render(&bb); } - glDepthMask(true); + + // restore + glDisable(GL_POLYGON_OFFSET_LINE); + RenderManager.StateSetColour(1.0f, 1.0f, 1.0f, 1.0f); glEnable(GL_TEXTURE_2D); - glDisable(GL_BLEND); + RenderManager.StateSetLightingEnable(true); } } void LevelRenderer::render(AABB* b) { Tesselator* t = Tesselator::getInstance(); + RenderManager.StateSetLightingEnable(false); + glDisable(GL_TEXTURE_2D); + RenderManager.StateSetColour(0.0f, 0.0f, 0.0f, 0.4f); - t->begin(GL_LINE_STRIP); - t->vertex((float)(b->x0), (float)(b->y0), (float)(b->z0)); - t->vertex((float)(b->x1), (float)(b->y0), (float)(b->z0)); - t->vertex((float)(b->x1), (float)(b->y0), (float)(b->z1)); - t->vertex((float)(b->x0), (float)(b->y0), (float)(b->z1)); - t->vertex((float)(b->x0), (float)(b->y0), (float)(b->z0)); - t->end(); - - t->begin(GL_LINE_STRIP); - t->vertex((float)(b->x0), (float)(b->y1), (float)(b->z0)); - t->vertex((float)(b->x1), (float)(b->y1), (float)(b->z0)); - t->vertex((float)(b->x1), (float)(b->y1), (float)(b->z1)); - t->vertex((float)(b->x0), (float)(b->y1), (float)(b->z1)); - t->vertex((float)(b->x0), (float)(b->y1), (float)(b->z0)); - t->end(); + // prevent zfight + glEnable(GL_POLYGON_OFFSET_LINE); + glPolygonOffset(-2.0f, -2.0f); + // One call please! t->begin(GL_LINES); - t->vertex((float)(b->x0), (float)(b->y0), (float)(b->z0)); - t->vertex((float)(b->x0), (float)(b->y1), (float)(b->z0)); - t->vertex((float)(b->x1), (float)(b->y0), (float)(b->z0)); - t->vertex((float)(b->x1), (float)(b->y1), (float)(b->z0)); - t->vertex((float)(b->x1), (float)(b->y0), (float)(b->z1)); - t->vertex((float)(b->x1), (float)(b->y1), (float)(b->z1)); - t->vertex((float)(b->x0), (float)(b->y0), (float)(b->z1)); - t->vertex((float)(b->x0), (float)(b->y1), (float)(b->z1)); + + // Bottom + t->vertex(b->x0, b->y0, b->z0); + t->vertex(b->x1, b->y0, b->z0); + t->vertex(b->x1, b->y0, b->z0); + t->vertex(b->x1, b->y0, b->z1); + t->vertex(b->x1, b->y0, b->z1); + t->vertex(b->x0, b->y0, b->z1); + t->vertex(b->x0, b->y0, b->z1); + t->vertex(b->x0, b->y0, b->z0); + + // Top + t->vertex(b->x0, b->y1, b->z0); + t->vertex(b->x1, b->y1, b->z0); + t->vertex(b->x1, b->y1, b->z0); + t->vertex(b->x1, b->y1, b->z1); + t->vertex(b->x1, b->y1, b->z1); + t->vertex(b->x0, b->y1, b->z1); + t->vertex(b->x0, b->y1, b->z1); + t->vertex(b->x0, b->y1, b->z0); + + // Vertical + t->vertex(b->x0, b->y0, b->z0); + t->vertex(b->x0, b->y1, b->z0); + t->vertex(b->x1, b->y0, b->z0); + t->vertex(b->x1, b->y1, b->z0); + t->vertex(b->x1, b->y0, b->z1); + t->vertex(b->x1, b->y1, b->z1); + t->vertex(b->x0, b->y0, b->z1); + t->vertex(b->x0, b->y1, b->z1); + t->end(); + glDisable(GL_POLYGON_OFFSET_LINE); + RenderManager.StateSetLightingEnable(true); + glEnable(GL_TEXTURE_2D); + RenderManager.StateSetColour(1.0f, 1.0f, 1.0f, 1.0f); } void LevelRenderer::setDirty(int x0, int y0, int z0, int x1, int y1, int z1, @@ -2766,13 +2802,16 @@ void LevelRenderer::waitForCull_SPU() { } #endif // __PS3__ +// 4jcraft: optional occlusion culling system, i hope to upgrade it soon +// gives better performances but mostly breaks chunk rendering void LevelRenderer::cull(Culler* culler, float a) { - int playerIndex = mc->player->GetXboxPad(); // 4J added + int playerIndex = mc->player->GetXboxPad(); + if (chunks[playerIndex].data == nullptr) return; #if defined __PS3__ && !defined DISABLE_SPU_CODE cull_SPU(playerIndex, culler, a); return; -#endif // __PS3__ +#endif FrustumCuller* fc = (FrustumCuller*)culler; FrustumData* fd = fc->frustum; @@ -2788,28 +2827,310 @@ void LevelRenderer::cull(Culler* culler, float a) { (fy * -fc->yOff) + (fz * -fc->zOff)); } - ClipChunk* pClipChunk = chunks[playerIndex].data; - int vis = 0; - int total = 0; - int numWrong = 0; +#if defined(OCCLUSION_MODE_NONE) + // just check if chunk is compiled and non-empty for (unsigned int i = 0; i < chunks[playerIndex].length; i++) { - unsigned char flags = pClipChunk->globalIdx == -1 - ? 0 - : globalChunkFlags[pClipChunk->globalIdx]; - - if ((flags & CHUNK_FLAG_COMPILED) && - ((flags & CHUNK_FLAG_EMPTYBOTH) != CHUNK_FLAG_EMPTYBOTH)) { - bool clipres = clip(pClipChunk->aabb, fdraw); - pClipChunk->visible = clipres; - if (pClipChunk->visible) vis++; - total++; - } else { - pClipChunk->visible = false; + ClipChunk* cc = &chunks[playerIndex][i]; + if (cc->globalIdx < 0) { + cc->visible = false; + continue; } - pClipChunk++; - } -} + unsigned char flags = globalChunkFlags[cc->globalIdx]; + bool isCompiled = (flags & CHUNK_FLAG_COMPILED) != 0; + bool isEmptyBoth = + (flags & CHUNK_FLAG_EMPTYBOTH) == CHUNK_FLAG_EMPTYBOTH; + + cc->visible = isCompiled && !isEmptyBoth; + } + +#elif defined(OCCLUSION_MODE_FRUSTUM) + // Just ~~monika~~ frustum culling + for (unsigned int i = 0; i < chunks[playerIndex].length; i++) { + ClipChunk* cc = &chunks[playerIndex][i]; + if (cc->globalIdx < 0) { + cc->visible = false; + continue; + } + + unsigned char flags = globalChunkFlags[cc->globalIdx]; + bool isCompiled = (flags & CHUNK_FLAG_COMPILED) != 0; + bool isEmptyBoth = + (flags & CHUNK_FLAG_EMPTYBOTH) == CHUNK_FLAG_EMPTYBOTH; + + if (isCompiled && !isEmptyBoth) { + float cellBounds[6] = {(float)cc->chunk->x - 0.1f, + (float)cc->chunk->y - 0.1f, + (float)cc->chunk->z - 0.1f, + (float)cc->chunk->x + CHUNK_XZSIZE + 0.1f, + (float)cc->chunk->y + CHUNK_SIZE + 0.1f, + (float)cc->chunk->z + CHUNK_XZSIZE + 0.1f}; + cc->visible = clip(cellBounds, fdraw); + } else { + cc->visible = false; + } + } + +#elif defined(OCCLUSION_MODE_HARDWARE) +// TODO: Hardware occlusion culling using GPU queries +// For now, fall back to frustum culling +#warning \ + "OCCLUSION_MODE_HARDWARE is not implemented yet, falling back to frustum culling" + for (unsigned int i = 0; i < chunks[playerIndex].length; i++) { + ClipChunk* cc = &chunks[playerIndex][i]; + if (cc->globalIdx < 0) { + cc->visible = false; + continue; + } + + unsigned char flags = globalChunkFlags[cc->globalIdx]; + bool isCompiled = (flags & CHUNK_FLAG_COMPILED) != 0; + bool isEmptyBoth = + (flags & CHUNK_FLAG_EMPTYBOTH) == CHUNK_FLAG_EMPTYBOTH; + + if (isCompiled && !isEmptyBoth) { + float cellBounds[6] = {(float)cc->chunk->x - 0.1f, + (float)cc->chunk->y - 0.1f, + (float)cc->chunk->z - 0.1f, + (float)cc->chunk->x + CHUNK_XZSIZE + 0.1f, + (float)cc->chunk->y + CHUNK_SIZE + 0.1f, + (float)cc->chunk->z + CHUNK_XZSIZE + 0.1f}; + cc->visible = clip(cellBounds, fdraw); + } else { + cc->visible = false; + } + } + +#elif defined(OCCLUSION_MODE_BFS) + // Experimental BFS occlusion culling. + // Check https://tomcc.github.io/2014/08/31/visibility-1.html + // And https://tomcc.github.io/2014/08/31/visibility-2.html + // And finally https://en.wikipedia.org/wiki/Breadth-first_search + std::shared_ptr player = mc->cameraTargetPlayer; + float camX = (float)(player->xOld + (player->x - player->xOld) * a); + float camY = (float)(player->yOld + (player->y - player->yOld) * a); + float camZ = (float)(player->zOld + (player->z - player->zOld) * a); + + auto intFloorDiv = [](int v, int div) { + if (v < 0 && v % div != 0) return (v / div) - 1; + return v / div; + }; + + auto floatFloorDiv = [](float v, int div) { + int iv = (int)v; + if (v < 0 && v != iv) iv--; + if (iv < 0 && iv % div != 0) return (iv / div) - 1; + return iv / div; + }; + + int minCx = INT_MAX, minCy = INT_MAX, minCz = INT_MAX; + int maxCx = INT_MIN, maxCy = INT_MIN, maxCz = INT_MIN; + + for (unsigned int i = 0; i < chunks[playerIndex].length; i++) { + ClipChunk* cc = &chunks[playerIndex][i]; + cc->visible = false; + if (cc->globalIdx < 0) continue; + + int cx = intFloorDiv(cc->chunk->x, CHUNK_XZSIZE); + int cy = intFloorDiv(cc->chunk->y, CHUNK_SIZE); + int cz = intFloorDiv(cc->chunk->z, CHUNK_XZSIZE); + + if (cx < minCx) minCx = cx; + if (cy < minCy) minCy = cy; + if (cz < minCz) minCz = cz; + if (cx > maxCx) maxCx = cx; + if (cy > maxCy) maxCy = cy; + if (cz > maxCz) maxCz = cz; + } + + if (minCx > maxCx) return; + + int sizeX = maxCx - minCx + 1; + int sizeY = maxCy - minCy + 1; + int sizeZ = maxCz - minCz + 1; + int gridSize = sizeX * sizeY * sizeZ; + + if (m_bfsGrid.size() < gridSize) { + m_bfsGrid.resize(gridSize); + } + + memset(m_bfsGrid.data(), 0, gridSize * sizeof(ClipChunk*)); + for (unsigned int i = 0; i < chunks[playerIndex].length; i++) { + ClipChunk* cc = &chunks[playerIndex][i]; + if (cc->globalIdx < 0) continue; + int lx = intFloorDiv(cc->chunk->x, CHUNK_XZSIZE) - minCx; + int ly = intFloorDiv(cc->chunk->y, CHUNK_SIZE) - minCy; + int lz = intFloorDiv(cc->chunk->z, CHUNK_XZSIZE) - minCz; + m_bfsGrid[(lx * sizeY + ly) * sizeZ + lz] = cc; + } + + auto getChunkAt = [&](int cx, int cy, int cz) -> ClipChunk* { + int lx = cx - minCx; + int ly = cy - minCy; + int lz = cz - minCz; + if (lx >= 0 && lx < sizeX && ly >= 0 && ly < sizeY && lz >= 0 && + lz < sizeZ) { + return m_bfsGrid[(lx * sizeY + ly) * sizeZ + lz]; + } + return nullptr; + }; + + int startCx = floatFloorDiv(camX, CHUNK_XZSIZE); + int startCy = floatFloorDiv(camY, CHUNK_SIZE); + int startCz = floatFloorDiv(camZ, CHUNK_XZSIZE); + + if (startCx < minCx) + startCx = minCx; + else if (startCx > maxCx) + startCx = maxCx; + if (startCy < minCy) + startCy = minCy; + else if (startCy > maxCy) + startCy = maxCy; + if (startCz < minCz) + startCz = minCz; + else if (startCz > maxCz) + startCz = maxCz; + + ClipChunk* startChunk = getChunkAt(startCx, startCy, startCz); + + if (!startChunk) { + float minDist = 1e30f; + for (unsigned int i = 0; i < chunks[playerIndex].length; i++) { + ClipChunk* cc = &chunks[playerIndex][i]; + if (cc->globalIdx < 0) continue; + float midX = cc->chunk->x + CHUNK_XZSIZE * 0.5f; + float midY = cc->chunk->y + CHUNK_SIZE * 0.5f; + float midZ = cc->chunk->z + CHUNK_XZSIZE * 0.5f; + float dist = (camX - midX) * (camX - midX) + + (camY - midY) * (camY - midY) + + (camZ - midZ) * (camZ - midZ); + if (dist < minDist) { + minDist = dist; + startChunk = cc; + } + } + } + + if (!startChunk) return; + + struct BFSNode { + ClipChunk* cc; + int incomingFace; + }; + + static thread_local std::vector q; + q.clear(); + q.reserve(chunks[playerIndex].length); + int qHead = 0; + + int visitedSize = chunks[playerIndex].length; + if (m_bfsVisitedFaces[playerIndex].size() < visitedSize) { + m_bfsVisitedFaces[playerIndex].resize(visitedSize, 0); + } + memset(m_bfsVisitedFaces[playerIndex].data(), 0, visitedSize); + + q.push_back({startChunk, -1}); + m_bfsVisitedFaces[playerIndex][startChunk - chunks[playerIndex].data] = + 0x3F; + + static const int OFFSETS[6][3] = { + {0, -1, 0}, // 0: -Y + {0, 1, 0}, // 1: +Y + {0, 0, -1}, // 2: -Z + {0, 0, 1}, // 3: +Z + {-1, 0, 0}, // 4: -X + {1, 0, 0} // 5: +X + }; + + while (qHead < (int)q.size()) { + BFSNode node = q[qHead++]; + ClipChunk* curr = node.cc; + int incFace = node.incomingFace; + + unsigned char flags = globalChunkFlags[curr->globalIdx]; + bool isCompiled = (flags & CHUNK_FLAG_COMPILED) != 0; + bool isEmptyBoth = + (flags & CHUNK_FLAG_EMPTYBOTH) == CHUNK_FLAG_EMPTYBOTH; + + if (isCompiled && !isEmptyBoth) { + curr->visible = true; + } + + int cx = intFloorDiv(curr->chunk->x, CHUNK_XZSIZE); + int cy = intFloorDiv(curr->chunk->y, CHUNK_SIZE); + int cz = intFloorDiv(curr->chunk->z, CHUNK_XZSIZE); + + uint64_t conn = getGlobalChunkConnectivity(curr->globalIdx); + + for (int i = 0; i < 6; i++) { + int outFace = i; + + bool canGo = false; + float chkX = curr->chunk->x; + float chkY = curr->chunk->y; + float chkZ = curr->chunk->z; + switch (outFace) { + case 0: + canGo = camY >= chkY; + break; + case 1: + canGo = camY <= chkY + CHUNK_SIZE; + break; + case 2: + canGo = camZ >= chkZ; + break; + case 3: + canGo = camZ <= chkZ + CHUNK_XZSIZE; + break; + case 4: + canGo = camX >= chkX; + break; + case 5: + canGo = camX <= chkX + CHUNK_XZSIZE; + break; + } + if (!canGo) continue; + + if (incFace != -1) { + int shift = (incFace * 6) + outFace; + if ((conn & (1ULL << shift)) == 0) continue; + } + + int nx = cx + OFFSETS[i][0]; + int ny = cy + OFFSETS[i][1]; + int nz = cz + OFFSETS[i][2]; + + ClipChunk* neighbor = getChunkAt(nx, ny, nz); + if (!neighbor) continue; + + int nIdx = neighbor - chunks[playerIndex].data; + int nextIncFace = outFace ^ 1; + + if ((m_bfsVisitedFaces[playerIndex][nIdx] & (1 << nextIncFace)) != + 0) + continue; + + float cellBounds[6] = { + (float)neighbor->chunk->x - 0.1f, + (float)neighbor->chunk->y - 0.1f, + (float)neighbor->chunk->z - 0.1f, + (float)neighbor->chunk->x + CHUNK_XZSIZE + 0.1f, + (float)neighbor->chunk->y + CHUNK_SIZE + 0.1f, + (float)neighbor->chunk->z + CHUNK_XZSIZE + 0.1f}; + + if (!clip(cellBounds, fdraw)) continue; + + m_bfsVisitedFaces[playerIndex][nIdx] |= (1 << nextIncFace); + q.push_back({neighbor, nextIncFace}); + } + } + +#else +#error \ + "Unknown occlusion mode, this should NEVER happen, check meson.build for misconfiguration" +#endif +} void LevelRenderer::playStreamingMusic(const std::wstring& name, int x, int y, int z) { if (name != L"") { @@ -3778,6 +4099,19 @@ void LevelRenderer::setGlobalChunkFlag(int x, int y, int z, Level* level, } } +void LevelRenderer::setGlobalChunkConnectivity(int index, uint64_t conn) { + if (index >= 0 && index < getGlobalChunkCount()) { + globalChunkConnectivity[index] = conn; + } +} + +uint64_t LevelRenderer::getGlobalChunkConnectivity(int index) { + if (index >= 0 && index < getGlobalChunkCount()) { + return globalChunkConnectivity[index]; + } + return ~(uint64_t)0; // out of bounds +} + void LevelRenderer::clearGlobalChunkFlag(int x, int y, int z, Level* level, unsigned char flag, unsigned char shift) { @@ -3842,16 +4176,88 @@ unsigned char LevelRenderer::decGlobalChunkRefCount(int x, int y, int z, } } +void LevelRenderer::queueRenderableTileEntityForRemoval_Locked( + int key, TileEntity* tileEntity) { + m_renderableTileEntitiesPendingRemoval[key].insert(tileEntity); +} + +void LevelRenderer::addRenderableTileEntity_Locked( + int key, const std::shared_ptr& tileEntity) { + RenderableTileEntityBucket& bucket = renderableTileEntities[key]; + TileEntity* tileEntityPtr = tileEntity.get(); + if (bucket.indexByTile.find(tileEntityPtr) != bucket.indexByTile.end()) { + return; + } + + size_t index = bucket.tiles.size(); + bucket.tiles.push_back(tileEntity); + bucket.indexByTile.insert(std::make_pair(tileEntityPtr, index)); +} + +void LevelRenderer::eraseRenderableTileEntity_Locked( + RenderableTileEntityBucket& bucket, TileEntity* tileEntity) { + auto it = bucket.indexByTile.find(tileEntity); + if (it == bucket.indexByTile.end()) { + return; + } + + size_t index = it->second; + size_t lastIndex = bucket.tiles.size() - 1; + if (index != lastIndex) { + std::shared_ptr moved = bucket.tiles[lastIndex]; + bucket.tiles[index] = moved; + bucket.indexByTile[moved.get()] = index; + } + + bucket.tiles.pop_back(); + bucket.indexByTile.erase(it); +} + +void LevelRenderer::retireRenderableTileEntitiesForChunkKey(int key) { + if (key == -1) return; + + EnterCriticalSection(&m_csRenderableTileEntities); + renderableTileEntities.erase(key); + m_renderableTileEntitiesPendingRemoval.erase(key); + LeaveCriticalSection(&m_csRenderableTileEntities); +} + // 4J added void LevelRenderer::fullyFlagRenderableTileEntitiesToBeRemoved() { + FRAME_PROFILE_SCOPE(RenderableTileEntityCleanup); + EnterCriticalSection(&m_csRenderableTileEntities); - AUTO_VAR(itChunkEnd, renderableTileEntities.end()); - for (AUTO_VAR(it, renderableTileEntities.begin()); it != itChunkEnd; it++) { - AUTO_VAR(itTEEnd, it->second.end()); - for (AUTO_VAR(it2, it->second.begin()); it2 != itTEEnd; it2++) { - (*it2)->upgradeRenderRemoveStage(); + if (m_renderableTileEntitiesPendingRemoval.empty()) { + LeaveCriticalSection(&m_csRenderableTileEntities); + return; + } + + auto itKeyEnd = m_renderableTileEntitiesPendingRemoval.end(); + for (auto itKey = m_renderableTileEntitiesPendingRemoval.begin(); + itKey != itKeyEnd; itKey++) { + auto itChunk = renderableTileEntities.find(itKey->first); + if (itChunk == renderableTileEntities.end()) continue; + + RenderableTileEntityBucket& bucket = itChunk->second; + for (AUTO_VAR(itPending, itKey->second.begin()); + itPending != itKey->second.end(); itPending++) { + if (bucket.indexByTile.find(*itPending) == + bucket.indexByTile.end()) { + continue; + } + + if (!(*itPending)->finalizeRenderRemoveStage()) { + continue; + } + + eraseRenderableTileEntity_Locked(bucket, *itPending); + } + + if (bucket.tiles.empty()) { + renderableTileEntities.erase(itChunk); } } + m_renderableTileEntitiesPendingRemoval.clear(); LeaveCriticalSection(&m_csRenderableTileEntities); } @@ -4041,7 +4447,10 @@ int LevelRenderer::rebuildChunkThreadProc(void* lpParam) { s_activationEventA[index]->WaitForSignal(INFINITE); // app.DebugPrintf("Rebuilding permaChunk %d\n", index + 1); - permaChunk[index + 1].rebuild(); + { + FRAME_PROFILE_SCOPE(ChunkRebuildBody); + permaChunk[index + 1].rebuild(); + } // Inform the producer thread that we are done with this chunk s_rebuildCompleteEvents->Set(index); diff --git a/Minecraft.Client/Rendering/LevelRenderer.h b/Minecraft.Client/Rendering/LevelRenderer.h index 58f194a3b..ec925ebbe 100644 --- a/Minecraft.Client/Rendering/LevelRenderer.h +++ b/Minecraft.Client/Rendering/LevelRenderer.h @@ -10,6 +10,7 @@ #ifdef __PS3__ #include "../Platform/PS3/PS3Extras/C4JSpursJob.h" #endif +#include class MultiPlayerLevel; class Textures; class Chunk; @@ -163,8 +164,13 @@ public: void destroyTileProgress(int id, int x, int y, int z, int progress); void registerTextures(IconRegister* iconRegister); - typedef std::unordered_map >, - IntKeyHash, IntKeyEq> + struct RenderableTileEntityBucket { + std::vector > tiles; + std::unordered_map indexByTile; + }; + + typedef std::unordered_map rteMap; private: @@ -176,6 +182,10 @@ private: rteMap renderableTileEntities; // 4J - changed - was // std::vector, // now hashed by chunk so we can find them + typedef std::unordered_set rtePendingRemovalSet; + typedef std::unordered_map + rtePendingRemovalMap; + rtePendingRemovalMap m_renderableTileEntitiesPendingRemoval; CRITICAL_SECTION m_csRenderableTileEntities; MultiPlayerLevel* level[4]; // 4J - now one per player Textures* textures; @@ -209,10 +219,21 @@ private: emptyChunks; static const int RENDERLISTS_LENGTH = 4; // 4J - added OffsettedRenderList renderLists[RENDERLISTS_LENGTH]; - + void setGlobalChunkConnectivity(int index, uint64_t conn); + uint64_t getGlobalChunkConnectivity(int index); + std::vector m_bfsGrid; + std::vector m_bfsVisitedFaces[4]; std::unordered_map destroyingBlocks; Icon** breakingTextures; + void addRenderableTileEntity_Locked( + int key, const std::shared_ptr& tileEntity); + void eraseRenderableTileEntity_Locked(RenderableTileEntityBucket& bucket, + TileEntity* tileEntity); + void queueRenderableTileEntityForRemoval_Locked(int key, + TileEntity* tileEntity); + void retireRenderableTileEntitiesForChunkKey(int key); + public: void fullyFlagRenderableTileEntitiesToBeRemoved(); // 4J added @@ -296,6 +317,8 @@ public: void clearGlobalChunkFlag(int x, int y, int z, Level* level, unsigned char flag, unsigned char shift = 0); + static uint64_t* globalChunkConnectivity; + // Get/set whole byte of flags unsigned char getGlobalChunkFlags(int x, int y, int z, Level* level); void setGlobalChunkFlags(int x, int y, int z, Level* level, diff --git a/Minecraft.Client/Rendering/Tesselator.cpp b/Minecraft.Client/Rendering/Tesselator.cpp index 49e6f5669..76e13d996 100644 --- a/Minecraft.Client/Rendering/Tesselator.cpp +++ b/Minecraft.Client/Rendering/Tesselator.cpp @@ -215,10 +215,15 @@ void Tesselator::end() { } #endif } - glDisableClientState(GL_VERTEX_ARRAY); - if (hasTexture) glDisableClientState(GL_TEXTURE_COORD_ARRAY); - if (hasColor) glDisableClientState(GL_COLOR_ARRAY); - if (hasNormal) glDisableClientState(GL_NORMAL_ARRAY); + // 4jcraft: gldisableclientstate breaks gl compat, commenting those lead + // to some weird glitches with input but.. somehow stopped one day so.. + // just keep an eye on these incase mouse locking stops working outta + // nowhere (i blame opengl not me) + // + // glDisableClientState(GL_VERTEX_ARRAY); if (hasTexture) + // glDisableClientState(GL_TEXTURE_COORD_ARRAY); if (hasColor) + // glDisableClientState(GL_COLOR_ARRAY); if (hasNormal) + // glDisableClientState(GL_NORMAL_ARRAY); } clear(); diff --git a/Minecraft.Client/Textures/Textures.cpp b/Minecraft.Client/Textures/Textures.cpp index 907f51927..a9927a1bf 100644 --- a/Minecraft.Client/Textures/Textures.cpp +++ b/Minecraft.Client/Textures/Textures.cpp @@ -541,11 +541,16 @@ void Textures::bindTextureLayers(ResourceLocation* resource) { } void Textures::bind(int id) { + // 4jcraft: Classic GUI code still performs some raw glBindTexture calls, so + // this path must always rebind rather than trusting lastBoundId to be in sync. + // TODO(4jcraft): Long term, route all texture binds through one synchronized + // path or invalidate lastBoundId at every raw glBindTexture call so this can + // safely use cached binds again without breaking font/UI rendering. // if (id != lastBoundId) { if (id < 0) return; glBindTexture(GL_TEXTURE_2D, id); - // lastBoundId = id; + // lastBoundId = id; } } @@ -696,8 +701,8 @@ void Textures::loadTexture(BufferedImage* img, int id, bool blur, bool clamp) { } if (clamp) { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); diff --git a/Minecraft.Client/Utils/FrameProfiler.cpp b/Minecraft.Client/Utils/FrameProfiler.cpp new file mode 100644 index 000000000..53ddaa42f --- /dev/null +++ b/Minecraft.Client/Utils/FrameProfiler.cpp @@ -0,0 +1,266 @@ +#include "../Platform/stdafx.h" +#include "FrameProfiler.h" + +#ifdef ENABLE_FRAME_PROFILER + +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +#define FRAME_PROFILER_NOINLINE __declspec(noinline) +#elif defined(__GNUC__) || defined(__clang__) +#define FRAME_PROFILER_NOINLINE __attribute__((noinline)) +#else +#define FRAME_PROFILER_NOINLINE +#endif + +namespace { + +using FrameProfilerClock = std::chrono::steady_clock; +using Bucket = FrameProfiler::Bucket; +constexpr std::uint64_t kNsPerMs = 1000ULL * 1000ULL; +constexpr std::uint64_t kReportIntervalNs = 1000ULL * 1000ULL * 1000ULL; +constexpr std::size_t kBucketCount = FrameProfiler::BucketCount(); +constexpr auto kFalseTokens = std::to_array({ + "0", "false", "False", "FALSE", "no", "No", "NO", "off", "Off", "OFF", +}); +constexpr std::array + kBucketDescriptors = {{ + {Bucket::Frame, "frame"}, + {Bucket::World, "world"}, + {Bucket::Terrain, "terrain"}, + {Bucket::ChunkCull, "chunkCull"}, + {Bucket::ChunkCollect, "chunkCollect"}, + {Bucket::ChunkPlayback, "chunkPlayback"}, + {Bucket::ChunkDirtyScan, "chunkDirtyScan"}, + {Bucket::ChunkRebuildSchedule, "chunkRebuildSchedule"}, + {Bucket::ChunkRebuildBody, "chunkRebuildBody"}, + {Bucket::ChunkPrepass, "chunkPrepass"}, + {Bucket::ChunkBlockShape, "chunkBlockShape"}, + {Bucket::ChunkBlockFaceCull, "chunkBlockFaceCull"}, + {Bucket::ChunkBlockLighting, "chunkBlockLighting"}, + {Bucket::ChunkBlockEmit, "chunkBlockEmit"}, + {Bucket::RenderableTileEntityCleanup, "renderableTileEntityCleanup"}, + {Bucket::TileEntityUnloadCleanup, "tileEntityUnloadCleanup"}, + {Bucket::Entity, "entities"}, + {Bucket::Particle, "particles"}, + {Bucket::WeatherSky, "weather"}, + {Bucket::UIHud, "ui"}, + {Bucket::Lightmap, "lightmap"}, + }}; + +struct BucketTotals { + std::uint64_t totalNs{}; + std::uint64_t maxNs{}; + std::uint64_t calls{}; + + void Record(std::uint64_t elapsedNs) noexcept { + totalNs += elapsedNs; + ++calls; + if (elapsedNs > maxNs) maxNs = elapsedNs; + } + + void Merge(const BucketTotals& other) noexcept { + totalNs += other.totalNs; + calls += other.calls; + if (other.maxNs > maxNs) maxNs = other.maxNs; + } +}; + +struct AtomicBucketTotals { + std::atomic totalNs{0}; + std::atomic maxNs{0}; + std::atomic calls{0}; +}; + +struct ProfilerState { + std::array workerBuckets{}; +}; + +struct ThreadState { + std::uint32_t frameScopeDepth{}; + std::uint64_t windowStartNs{}; + std::array localBuckets{}; +}; + +constinit ProfilerState g_profilerState{}; +constinit thread_local ThreadState t_threadState{}; + +static_assert(kBucketDescriptors.size() == kBucketCount); + +[[nodiscard]] inline std::uint64_t nowNs() noexcept { + return static_cast( + std::chrono::duration_cast( + FrameProfilerClock::now().time_since_epoch()) + .count()); +} + +[[nodiscard]] constexpr double nsToMs(std::uint64_t ns) noexcept { + return static_cast(ns) / static_cast(kNsPerMs); +} + +[[nodiscard]] constexpr bool envSaysDisabled( + std::string_view value) noexcept { + if (value.empty()) return false; + + for (std::string_view falseToken : kFalseTokens) { + if (value == falseToken) return true; + } + return false; +} + +inline void updateAtomicMax(std::atomic& value, + std::uint64_t candidate) noexcept { + std::uint64_t current = value.load(std::memory_order_relaxed); + while (current < candidate && + !value.compare_exchange_weak(current, candidate, + std::memory_order_relaxed, + std::memory_order_relaxed)) { + } +} + +inline void recordWorkerBucket(Bucket bucket, std::uint64_t elapsedNs) noexcept { + AtomicBucketTotals& state = + g_profilerState.workerBuckets[FrameProfiler::BucketIndex(bucket)]; + state.totalNs.fetch_add(elapsedNs, std::memory_order_relaxed); + state.calls.fetch_add(1, std::memory_order_relaxed); + updateAtomicMax(state.maxNs, elapsedNs); +} + +[[nodiscard]] inline bool isFrameThread() noexcept { + return t_threadState.frameScopeDepth != 0; +} + +FRAME_PROFILER_NOINLINE bool computeEnabled() noexcept { + const char* const envValue = std::getenv("C4J_FRAME_PROFILER"); + if (envValue == nullptr) return true; + return !envSaysDisabled(envValue); +} + +FRAME_PROFILER_NOINLINE void emitWindowReport( + const std::array& buckets) noexcept { + const std::uint64_t frames = + buckets[FrameProfiler::BucketIndex(Bucket::Frame)].calls; + if (frames == 0) return; + + std::fprintf(stderr, + "[frame-prof] avg/frame(ms) frames=%llu", + static_cast(frames)); + for (const auto& descriptor : kBucketDescriptors) { + const BucketTotals& bucket = + buckets[FrameProfiler::BucketIndex(descriptor.bucket)]; + const std::string_view label = descriptor.label; + std::fprintf(stderr, " %.*s=%.2f", static_cast(label.size()), + label.data(), nsToMs(bucket.totalNs) / frames); + } + std::fputc('\n', stderr); + + std::fputs("[frame-prof] max(ms)/calls", stderr); + for (const auto& descriptor : kBucketDescriptors) { + const BucketTotals& bucket = + buckets[FrameProfiler::BucketIndex(descriptor.bucket)]; + const std::string_view label = descriptor.label; + std::fprintf(stderr, " %.*s=%.2f/%llu", + static_cast(label.size()), label.data(), + nsToMs(bucket.maxNs), + static_cast(bucket.calls)); + } + std::fputc('\n', stderr); + std::fflush(stderr); +} + +[[nodiscard]] std::array snapshotAndResetWorkerBuckets() + noexcept { + std::array snapshot = {}; + for (std::size_t i = 0; i < kBucketCount; ++i) { + AtomicBucketTotals& workerBucket = g_profilerState.workerBuckets[i]; + snapshot[i].totalNs = + workerBucket.totalNs.exchange(0, std::memory_order_relaxed); + snapshot[i].maxNs = + workerBucket.maxNs.exchange(0, std::memory_order_relaxed); + snapshot[i].calls = + workerBucket.calls.exchange(0, std::memory_order_relaxed); + } + return snapshot; +} + +} // namespace + +bool FrameProfiler::IsEnabled() noexcept { + static const bool enabled = computeEnabled(); + return enabled; +} + +void FrameProfiler::Record(Bucket bucket, std::uint64_t elapsedNs) noexcept { + if (isFrameThread()) { + t_threadState.localBuckets[BucketIndex(bucket)].Record(elapsedNs); + return; + } + + recordWorkerBucket(bucket, elapsedNs); +} + +void FrameProfiler::EndFrame(std::uint64_t elapsedNs) noexcept { + Record(Bucket::Frame, elapsedNs); + + ThreadState& threadState = t_threadState; + const std::uint64_t now = nowNs(); + + if (threadState.windowStartNs == 0) { + threadState.windowStartNs = now; + return; + } + + if ((now - threadState.windowStartNs) < kReportIntervalNs) return; + + std::array combined = threadState.localBuckets; + const auto workerSnapshot = snapshotAndResetWorkerBuckets(); + + for (std::size_t i = 0; i < kBucketCount; ++i) { + combined[i].Merge(workerSnapshot[i]); + } + + emitWindowReport(combined); + + threadState.windowStartNs = now; + threadState.localBuckets = {}; +} + +FrameProfiler::Scope::Scope(Bucket bucket) noexcept + : m_startNs(0), m_bucket(bucket), m_enabled(FrameProfiler::IsEnabled()) { + if (m_enabled) m_startNs = nowNs(); +} + +FrameProfiler::Scope::~Scope() noexcept { + if (!m_enabled) return; + FrameProfiler::Record(m_bucket, nowNs() - m_startNs); +} + +FrameProfiler::FrameScope::FrameScope() noexcept + : m_startNs(0), m_enabled(false) { + if (!FrameProfiler::IsEnabled()) return; + + m_enabled = (t_threadState.frameScopeDepth++ == 0); + if (m_enabled) m_startNs = nowNs(); +} + +FrameProfiler::FrameScope::~FrameScope() noexcept { + if (!m_enabled) { + if (t_threadState.frameScopeDepth > 0) { + --t_threadState.frameScopeDepth; + } + return; + } + + FrameProfiler::EndFrame(nowNs() - m_startNs); + + if (t_threadState.frameScopeDepth > 0) { + --t_threadState.frameScopeDepth; + } +} + +#endif diff --git a/Minecraft.Client/Utils/FrameProfiler.h b/Minecraft.Client/Utils/FrameProfiler.h new file mode 100644 index 000000000..c22351f69 --- /dev/null +++ b/Minecraft.Client/Utils/FrameProfiler.h @@ -0,0 +1,100 @@ +#pragma once + +#ifdef ENABLE_FRAME_PROFILER + +#include +#include +#include + +class FrameProfiler { +public: + enum class Bucket : std::uint8_t { + Frame, + World, + Terrain, + ChunkCull, + ChunkCollect, + ChunkPlayback, + ChunkDirtyScan, + ChunkRebuildSchedule, + ChunkRebuildBody, + ChunkPrepass, + ChunkBlockShape, + ChunkBlockFaceCull, + ChunkBlockLighting, + ChunkBlockEmit, + RenderableTileEntityCleanup, + TileEntityUnloadCleanup, + Entity, + Particle, + WeatherSky, + UIHud, + Lightmap, + Count, + }; + + struct BucketDescriptor { + Bucket bucket; + const char* label; + }; + + [[nodiscard]] static constexpr std::size_t BucketIndex( + Bucket bucket) noexcept { + return static_cast(std::to_underlying(bucket)); + } + + [[nodiscard]] static constexpr std::size_t BucketCount() noexcept { + return BucketIndex(Bucket::Count); + } + + [[nodiscard]] static bool IsEnabled() noexcept; + + class Scope { + public: + explicit Scope(Bucket bucket) noexcept; + Scope(const Scope&) = delete; + Scope& operator=(const Scope&) = delete; + Scope(Scope&&) = delete; + Scope& operator=(Scope&&) = delete; + ~Scope() noexcept; + + private: + std::uint64_t m_startNs; + Bucket m_bucket; + bool m_enabled; + }; + + class FrameScope { + public: + FrameScope() noexcept; + FrameScope(const FrameScope&) = delete; + FrameScope& operator=(const FrameScope&) = delete; + FrameScope(FrameScope&&) = delete; + FrameScope& operator=(FrameScope&&) = delete; + ~FrameScope() noexcept; + + private: + std::uint64_t m_startNs; + bool m_enabled; + }; + +private: + static void Record(Bucket bucket, std::uint64_t elapsedNs) noexcept; + static void EndFrame(std::uint64_t elapsedNs) noexcept; +}; + +#define FRAME_PROFILE_CONCAT_INNER(a, b) a##b +#define FRAME_PROFILE_CONCAT(a, b) FRAME_PROFILE_CONCAT_INNER(a, b) +#define FRAME_PROFILE_SCOPE(bucket_name) \ + FrameProfiler::Scope FRAME_PROFILE_CONCAT(frameProfileScope_, __LINE__)( \ + FrameProfiler::Bucket::bucket_name) +#define FRAME_PROFILE_FRAME_SCOPE() \ + FrameProfiler::FrameScope FRAME_PROFILE_CONCAT(frameProfileFrameScope_, \ + __LINE__) + +#else + +#define FRAME_PROFILE_SCOPE(bucket_name) ((void)0) +#define FRAME_PROFILE_FRAME_SCOPE() ((void)0) + +#endif diff --git a/Minecraft.Client/meson.build b/Minecraft.Client/meson.build index 9bb614fc4..123320903 100644 --- a/Minecraft.Client/meson.build +++ b/Minecraft.Client/meson.build @@ -8,21 +8,21 @@ exclude_sources = [ # all sources except ./Platform/* client_sources = run_command( - 'sh', '-c', - 'find "' - + meson.current_source_dir() - + '" \\( -name "*.cpp" -o -name "*.c" \\)' - + ' '.join(exclude_sources), - check : true, + 'sh', + '-c', 'find "' + + meson.current_source_dir() + + '" \\( -name "*.cpp" -o -name "*.c" \\)' + + ' '.join(exclude_sources), + check: true, ).stdout().strip().split('\n') # all sources in ./Platform (top-level files only) platform_sources = run_command( - 'sh', '-c', - 'find "' - + meson.current_source_dir() / 'Platform' - + '" -maxdepth 1 \\( -name "*.cpp" -o -name "*.c" \\)', - check : true, + 'sh', + '-c', 'find "' + + meson.current_source_dir() / 'Platform' + + '" -maxdepth 1 \\( -name "*.cpp" -o -name "*.c" \\)', + check: true, ).stdout().strip().split('\n') # some platform-specific sources that are for some stupid reason in Common @@ -32,88 +32,109 @@ exclude_platform_common_sources = [ # we use system zlib instead, since this one is old as hell and isn't configured for linux correctly ' ! -path "*/zlib/*"', ' ! -name "SonyLeaderboardManager.cpp"', - ' ! -name "UIScene_InGameSaveManagementMenu.cpp"' + ' ! -name "UIScene_InGameSaveManagementMenu.cpp"', ] # all sources in in ./Platform/Common platform_sources += run_command( - 'sh', '-c', - 'find "' - + meson.current_source_dir() / 'Platform/Common' - + '" \\( -name "*.cpp" -o -name "*.c" \\)' - + ' '.join(exclude_platform_common_sources), - check : true, + 'sh', + '-c', 'find "' + + meson.current_source_dir() / 'Platform/Common' + + '" \\( -name "*.cpp" -o -name "*.c" \\)' + + ' '.join(exclude_platform_common_sources), + check: true, ).stdout().strip().split('\n') # linux-specific files (everything in Platform/Linux) if host_machine.system() == 'linux' platform_sources += run_command( - 'sh', '-c', - 'find "' - + meson.current_source_dir() / 'Platform/Linux' - + '" \\( -name "*.cpp" -o -name "*.c" \\) ', - check : true, + 'sh', + '-c', 'find "' + + meson.current_source_dir() / 'Platform/Linux' + + '" \\( -name "*.cpp" -o -name "*.c" \\) ', + check: true, ).stdout().strip().split('\n') endif client_dependencies = [ - render_dep, - input_dep, - profile_dep, - storage_dep, - assets_localisation_dep, - world_dep, - gl_dep, - glu_dep, - thread_dep, - thread_dep, - dependency('zlib'), - miniaudio_dep + render_dep, + input_dep, + profile_dep, + storage_dep, + assets_localisation_dep, + world_dep, + gl_dep, + glu_dep, + thread_dep, + dl_dep, + dependency('zlib'), + miniaudio_dep, + stb_dep, ] if get_option('enable_vsync') - global_cpp_defs += '-DENABLE_VSYNC' + global_cpp_defs += ['-DENABLE_VSYNC'] endif if get_option('classic_panorama') global_cpp_defs += '-DCLASSIC_PANORAMA' endif -if get_option('ui_backend') == 'shiggy' - shiggy_dep = dependency( - 'shiggy', - fallback : ['shiggy', 'shiggy_dep'], - ) +if get_option('enable_frame_profiler') + global_cpp_defs += ['-DENABLE_FRAME_PROFILER'] +endif - global_cpp_defs += '-D_ENABLEIGGY' - client_dependencies += shiggy_dep +if get_option('ui_backend') == 'shiggy' + shiggy_dep = dependency( + 'shiggy', + fallback: ['shiggy', 'shiggy_dep'], + ) + + global_cpp_defs += ['-D_ENABLEIGGY'] + client_dependencies += shiggy_dep endif if get_option('ui_backend') == 'java' global_cpp_defs += '-DENABLE_JAVA_GUIS' endif -client = executable('Minecraft.Client', +occlusion_mode = get_option('occlusion_culling') +if occlusion_mode == 'off' + global_cpp_defs += ['-DOCCLUSION_MODE_NONE'] +elif occlusion_mode == 'frustum' + global_cpp_defs += ['-DOCCLUSION_MODE_FRUSTUM'] +elif occlusion_mode == 'bfs' + global_cpp_defs += ['-DOCCLUSION_MODE_BFS', '-DUSE_OCCLUSION_CULLING'] +elif occlusion_mode == 'hardware' + global_cpp_defs += ['-DOCCLUSION_MODE_HARDWARE', '-DUSE_OCCLUSION_CULLING'] +endif +client = executable( + 'Minecraft.Client', client_sources + platform_sources + localisation[1], - include_directories : [include_directories('Platform', 'Platform/Linux/Iggy/include'),stb], - dependencies : client_dependencies, - cpp_args : global_cpp_args + global_cpp_defs + [ - '-DUNICODE', '-D_UNICODE', + include_directories: include_directories('Platform', 'Platform/Linux/Iggy/include'), + dependencies: client_dependencies, + cpp_args: global_cpp_args + + global_cpp_defs + + [ + '-DUNICODE', + '-D_UNICODE', '-include', meson.current_source_dir() / 'Platform/stdafx.h', ], - c_args : global_cpp_defs + ['-DUNICODE', '-D_UNICODE'], - install : true, - install_dir : '' + c_args: global_cpp_defs + ['-DUNICODE', '-D_UNICODE'], + install: true, + install_dir: '', ) # To support actually running the client from the build folder, we need to # copy the generated assets from Minecraft.Assets into the working directory # of the client. -custom_target('copy_assets_to_client', +custom_target( + 'copy_assets_to_client', input: [client, media_archive], output: 'assets.stamp', # using a stamp file to avoid copying assets every time - command : [ - python, meson.project_source_root() / 'scripts/copy_assets_to_client.py', + command: [ + python, + meson.project_source_root() / 'scripts/copy_assets_to_client.py', meson.project_source_root(), meson.project_build_root(), meson.current_build_dir(), diff --git a/Minecraft.World/Blocks/TileEntities/TileEntity.cpp b/Minecraft.World/Blocks/TileEntities/TileEntity.cpp index 24986046f..66ae1afac 100644 --- a/Minecraft.World/Blocks/TileEntities/TileEntity.cpp +++ b/Minecraft.World/Blocks/TileEntities/TileEntity.cpp @@ -187,6 +187,15 @@ void TileEntity::upgradeRenderRemoveStage() { } } +bool TileEntity::finalizeRenderRemoveStage() { + if (renderRemoveStage == e_RenderRemoveStageFlaggedAtChunk) { + renderRemoveStage = e_RenderRemoveStageRemove; + return true; + } + + return renderRemoveStage == e_RenderRemoveStageRemove; +} + // 4J Added void TileEntity::clone(std::shared_ptr tileEntity) { tileEntity->level = this->level; @@ -195,4 +204,4 @@ void TileEntity::clone(std::shared_ptr tileEntity) { tileEntity->z = this->z; tileEntity->data = this->data; tileEntity->tile = this->tile; -} \ No newline at end of file +} diff --git a/Minecraft.World/Blocks/TileEntities/TileEntity.h b/Minecraft.World/Blocks/TileEntities/TileEntity.h index e640ae2e4..fc382b673 100644 --- a/Minecraft.World/Blocks/TileEntities/TileEntity.h +++ b/Minecraft.World/Blocks/TileEntities/TileEntity.h @@ -49,6 +49,7 @@ public: void setRenderRemoveStage(unsigned char stage); // 4J added void upgradeRenderRemoveStage(); // 4J added + bool finalizeRenderRemoveStage(); // 4J added bool shouldRemoveForRender(); // 4J added virtual Level* getLevel(); @@ -77,4 +78,4 @@ public: protected: void clone(std::shared_ptr tileEntity); -}; \ No newline at end of file +}; diff --git a/Minecraft.World/Level/Level.cpp b/Minecraft.World/Level/Level.cpp index ab251a833..06c99a2ae 100644 --- a/Minecraft.World/Level/Level.cpp +++ b/Minecraft.World/Level/Level.cpp @@ -38,6 +38,7 @@ #include "../../Minecraft.Client/Platform/Common/DLC/DLCPack.h" #include "../../Minecraft.Client/Platform/PS3/PS3Extras/ShutdownManager.h" #include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/Utils/FrameProfiler.h" #include #include #include @@ -2206,19 +2207,11 @@ void Level::tickEntities() { // 4J-PB - Stuart - check this is correct here if (!tileEntitiesToUnload.empty()) { - // tileEntityList.removeAll(tileEntitiesToUnload); + FRAME_PROFILE_SCOPE(TileEntityUnloadCleanup); for (AUTO_VAR(it, tileEntityList.begin()); it != tileEntityList.end();) { - bool found = false; - for (AUTO_VAR(it2, tileEntitiesToUnload.begin()); - it2 != tileEntitiesToUnload.end(); it2++) { - if ((*it) == (*it2)) { - found = true; - break; - } - } - if (found) { + if (tileEntitiesToUnload.find(*it) != tileEntitiesToUnload.end()) { if (isClientSide) { __debugbreak(); } @@ -2731,7 +2724,9 @@ void Level::removeTileEntity(int x, int y, int z) { } void Level::markForRemoval(std::shared_ptr entity) { - tileEntitiesToUnload.push_back(entity); + EnterCriticalSection(&m_tileEntityListCS); + tileEntitiesToUnload.insert(entity); + LeaveCriticalSection(&m_tileEntityListCS); } bool Level::isSolidRenderTile(int x, int y, int z) { diff --git a/Minecraft.World/Level/Level.h b/Minecraft.World/Level/Level.h index d28d1cda5..50910ada0 100644 --- a/Minecraft.World/Level/Level.h +++ b/Minecraft.World/Level/Level.h @@ -10,6 +10,7 @@ #include "../WorldGen/Biomes/Biome.h" #include "../Util/C4JThread.h" #include +#include #ifdef __PSVITA__ #include "../../Minecraft.Client/Platform/PSVita/PSVitaExtras/CustomSet.h" @@ -118,7 +119,7 @@ public: private: std::vector > pendingTileEntities; - std::vector > tileEntitiesToUnload; + std::unordered_set > tileEntitiesToUnload; bool updatingTileEntities; public: @@ -665,3 +666,4 @@ public: bool canCreateMore(eINSTANCEOF type, ESPAWN_TYPE spawnType); }; +#include diff --git a/Minecraft.World/Level/LevelChunk.cpp b/Minecraft.World/Level/LevelChunk.cpp index 5f95fd9d2..52193665f 100644 --- a/Minecraft.World/Level/LevelChunk.cpp +++ b/Minecraft.World/Level/LevelChunk.cpp @@ -1558,13 +1558,19 @@ void LevelChunk::unload(bool unloadTileEntities) // 4J - added parameter { loaded = false; if (unloadTileEntities) { + std::vector > tileEntitiesToRemove; EnterCriticalSection(&m_csTileEntities); for (AUTO_VAR(it, tileEntities.begin()); it != tileEntities.end(); it++) { - // 4J-PB -m 1.7.3 was it->second->setRemoved(); - level->markForRemoval(it->second); + tileEntitiesToRemove.push_back(it->second); } LeaveCriticalSection(&m_csTileEntities); + + AUTO_VAR(itEnd, tileEntitiesToRemove.end()); + for (AUTO_VAR(it, tileEntitiesToRemove.begin()); it != itEnd; it++) { + // 4J-PB -m 1.7.3 was it->second->setRemoved(); + level->markForRemoval(*it); + } } #ifdef _ENTITIES_RW_SECTION diff --git a/flake.nix b/flake.nix index fd8af4473..05ceb4fef 100644 --- a/flake.nix +++ b/flake.nix @@ -54,6 +54,7 @@ version = "0.1.0"; src = ./.; + dontFixup = true; dontUseCmakeConfigure = true; # 4jcraft - Meson expects this subprojects structure @@ -100,6 +101,7 @@ openssl.dev libGL libGLU + glm SDL2 zlib ]; diff --git a/meson.build b/meson.build index cd2b7daa8..1939a88ad 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,9 @@ -project('4jcraft-chucklegrounds', ['cpp', 'c'], - version : '0.1.0', +project( + '4jcraft', + ['cpp', 'c'], + version: '0.1.0', meson_version: '>= 1.7', - default_options : [ + default_options: [ 'cpp_std=c++23', 'warning_level=0', 'buildtype=debug', # for now @@ -16,22 +18,8 @@ python = pymod.find_installation('python3', required: true) cc = meson.get_compiler('cpp') -# system deps -gl_dep = dependency('gl') -glu_dep = dependency('glu') -sdl2_dep = dependency('sdl2') # Yes.. i know sdl3 is out, but there's not point upgrading right now except when - # someone is gonna ask me "Hey juicey can you make it SDL3?" and i'd be like fuck you and still do it. -thread_dep = dependency('threads') -miniaudio_dep = dependency('miniaudio') -stb = subproject('stb').get_variable('stb_inc') -# compile flags (chagne ts juicey) -global_cpp_args = [ - '-fpermissive', - '-Wshift-count-overflow', - '-pipe', # use pipes instead of temp files between compiler stages -] +use_gles = get_option('renderer') == 'gles' -# global ccp defs type shi global_cpp_defs = [ '-DSPLIT_SAVES', '-D_LARGE_WORLDS', @@ -42,18 +30,61 @@ global_cpp_defs = [ ] if host_machine.system() == 'linux' - global_cpp_defs += [ - '-Dlinux', - '-D__linux', - '-D__linux__', - ] + global_cpp_defs += ['-Dlinux', '-D__linux', '-D__linux__'] endif -render_dep = dependency('4j-render', fallback: ['4jlibs', 'render_dep']) -input_dep = dependency('4j-input', fallback: ['4jlibs', 'input_dep']) -profile_dep = dependency('4j-profile', fallback: ['4jlibs', 'profile_dep']) -storage_dep = dependency('4j-storage', fallback: ['4jlibs', 'storage_dep']) +if use_gles + global_cpp_defs += ['-DGLES'] +endif + +add_project_arguments(global_cpp_defs, language: ['cpp', 'c']) + +global_cpp_args = [ + '-fpermissive', + '-Wshift-count-overflow', + '-pipe', +] +add_project_arguments(global_cpp_args, language: 'cpp') + +sdl2_dep = dependency('sdl2') +thread_dep = dependency('threads') +dl_dep = cc.find_library('dl', required: true) + +# GLES vs Desktop GL +if use_gles + gl_dep = dependency('glesv2', required: true) + glu_dep = dependency('', required: false) +else + gl_dep = dependency('gl', required: true) + glu_dep = dependency('glu', required: true) +endif + +stb = subproject('stb').get_variable('stb_inc') +stb_dep = declare_dependency(include_directories: stb) + +miniaudio_dep = dependency('miniaudio') + +sub_opts = ['gles=' + use_gles.to_string()] + +render_dep = dependency('4j-render', fallback: ['4jlibs', 'render_dep'], default_options: sub_opts) +input_dep = dependency('4j-input', fallback: ['4jlibs', 'input_dep'], default_options: sub_opts) +profile_dep = dependency('4j-profile', fallback: ['4jlibs', 'profile_dep'], default_options: sub_opts) +storage_dep = dependency('4j-storage', fallback: ['4jlibs', 'storage_dep'], default_options: sub_opts) + +all_deps = [ + gl_dep, + glu_dep, + sdl2_dep, + thread_dep, + dl_dep, + stb_dep, + miniaudio_dep, + render_dep, + input_dep, + profile_dep, + storage_dep, +] subdir('Minecraft.Assets') subdir('Minecraft.World') -subdir('Minecraft.Client') +subdir('Minecraft.Client') \ No newline at end of file diff --git a/meson.options b/meson.options index 68eb92598..4dc73f322 100644 --- a/meson.options +++ b/meson.options @@ -1,15 +1,42 @@ -option('ui_backend', - type : 'combo', +option( + 'ui_backend', + type: 'combo', choices: ['shiggy', 'java'], - value : 'shiggy', - description : 'Specifies a backend implementation for the game UI.') + value: 'shiggy', + description: 'Specifies a backend implementation for the game UI.', +) option('classic_panorama', type : 'boolean', value : false, description : 'Enable classic java edition panorama (ui_backend=java ONLY).') -option('enable_vsync', - type : 'boolean', - value : true, - description : 'Toggles V-Sync and adds options to unlock maximum in-game framerate.') +option( + 'renderer', + type: 'combo', + choices: ['gl3', 'gles'], + value: 'gl3', + description: 'Specifies a rendering implementation for the game.', +) + +option( + 'enable_vsync', + type: 'boolean', + value: true, + description: 'Toggles V-Sync and adds options to unlock maximum in-game framerate.', +) + +option( + 'enable_frame_profiler', + type: 'boolean', + value: false, + description: 'Enable the in-engine frame profiler for render hotspot discovery.', +) + +option( + 'occlusion_culling', + type: 'combo', + choices: ['off', 'frustum', 'bfs', 'hardware'], + value: 'frustum', + description: 'Occlusion culling mode. Off disables ALL CULLING (debug only!), Frustum disables offscreen rendering (default), BFS is experimental connectivity culling, hardware uses GPU queries.', +) diff --git a/subprojects/4jlibs.wrap b/subprojects/4jlibs.wrap index e9e21334c..ab584a950 100644 --- a/subprojects/4jlibs.wrap +++ b/subprojects/4jlibs.wrap @@ -1,6 +1,6 @@ [wrap-git] url = https://github.com/4jcraft/4jlibs.git -revision = main +revision = immediategone-v2 [provide] dependency_names = 4j-render, 4j-input, 4j-profile, 4j-storage