From 5f0088ab8b8885279407104093aed13e45cbfb0b Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Sun, 9 Oct 2016 12:50:57 +0200 Subject: [PATCH 1/7] Created new OpenGL framebuffer using the software renderer's hardware acceleration --- src/CMakeLists.txt | 1 + src/gl/system/gl_swframebuffer.cpp | 3357 ++++++++++++++++++++++++++++ src/gl/system/gl_swframebuffer.h | 470 ++++ src/textures/textures.h | 1 + 4 files changed, 3829 insertions(+) create mode 100644 src/gl/system/gl_swframebuffer.cpp create mode 100644 src/gl/system/gl_swframebuffer.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index de3316ced..dd241a3d7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1151,6 +1151,7 @@ set( FASTMATH_SOURCES gl/shaders/gl_fxaashader.cpp gl/system/gl_interface.cpp gl/system/gl_framebuffer.cpp + gl/system/gl_swframebuffer.cpp gl/system/gl_debug.cpp gl/system/gl_menu.cpp gl/system/gl_wipe.cpp diff --git a/src/gl/system/gl_swframebuffer.cpp b/src/gl/system/gl_swframebuffer.cpp new file mode 100644 index 000000000..e0ff2be47 --- /dev/null +++ b/src/gl/system/gl_swframebuffer.cpp @@ -0,0 +1,3357 @@ +/* +** gl_swframebuffer.cpp +** Code to let ZDoom use OpenGL as a simple framebuffer +** +**--------------------------------------------------------------------------- +** Copyright 1998-2011 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +** This file does _not_ implement hardware-acclerated 3D rendering. It is +** just a means of getting the pixel data to the screen in a more reliable +** method on modern hardware by copying the entire frame to a texture, +** drawing that to the screen, and presenting. +** +** That said, it does implement hardware-accelerated 2D rendering. +*/ + +#include "gl/system/gl_system.h" +#include "files.h" +#include "m_swap.h" +#include "v_video.h" +#include "doomstat.h" +#include "m_png.h" +#include "m_crc32.h" +#include "vectors.h" +#include "v_palette.h" +#include "templates.h" + +#include "c_dispatch.h" +#include "templates.h" +#include "i_system.h" +#include "i_video.h" +#include "i_input.h" +#include "v_pfx.h" +#include "stats.h" +#include "doomerrors.h" +#include "r_main.h" +#include "r_data/r_translate.h" +#include "f_wipe.h" +#include "sbar.h" +#include "w_wad.h" +#include "r_data/colormaps.h" + +#include "gl/system/gl_interface.h" +#include "gl/system/gl_swframebuffer.h" +#include "gl/data/gl_data.h" +#include "gl/utility/gl_clock.h" +#include "gl/utility/gl_templates.h" +#include "gl/gl_functions.h" + +CVAR(Bool, gl_antilag, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +CVAR(Int, gl_showpacks, 0, 0) +#ifndef WIN32 // Defined in fb_d3d9 for Windows +CVAR(Bool, vid_hwaalines, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +#else +EXTERN_CVAR(Bool, vid_hwaalines) +#endif + +EXTERN_CVAR(Bool, vid_hw2d) +EXTERN_CVAR(Bool, fullscreen) +EXTERN_CVAR(Float, Gamma) +EXTERN_CVAR(Bool, vid_vsync) +EXTERN_CVAR(Float, transsouls) +EXTERN_CVAR(Int, vid_refreshrate) + +extern cycle_t BlitCycles; + +IMPLEMENT_CLASS(OpenGLSWFrameBuffer) + +const char *const OpenGLSWFrameBuffer::ShaderNames[OpenGLSWFrameBuffer::NUM_SHADERS] = +{ + "NormalColor.fp", + "NormalColorPal.fp", + "NormalColorInv.fp", + "NormalColorPalInv.fp", + + "RedToAlpha.fp", + "RedToAlphaInv.fp", + + "VertexColor.fp", + + "SpecialColormap.fp", + "SpecialColorMapPal.fp", + + "InGameColormap.fp", + "InGameColormapDesat.fp", + "InGameColormapInv.fp", + "InGameColormapInvDesat.fp", + "InGameColormapPal.fp", + "InGameColormapPalDesat.fp", + "InGameColormapPalInv.fp", + "InGameColormapPalInvDesat.fp", + + "BurnWipe.fp", + "GammaCorrection.fp", +}; + +OpenGLSWFrameBuffer::OpenGLSWFrameBuffer(void *hMonitor, int width, int height, int bits, int refreshHz, bool fullscreen) : + Super(hMonitor, width, height, bits, refreshHz, fullscreen) +{ + // SetVSync needs to be at the very top to workaround a bug in Nvidia's OpenGL driver. + // If wglSwapIntervalEXT is called after glBindFramebuffer in a frame the setting is not changed! + SetVSync(vid_vsync); + + VertexBuffer = nullptr; + IndexBuffer = nullptr; + FBTexture = nullptr; + TempRenderTexture = nullptr; + RenderTexture[0] = nullptr; + RenderTexture[1] = nullptr; + InitialWipeScreen = nullptr; + ScreenshotTexture = nullptr; + ScreenshotSurface = nullptr; + FinalWipeScreen = nullptr; + PaletteTexture = nullptr; + GammaTexture = nullptr; + FrontCopySurface = nullptr; + for (int i = 0; i < NUM_SHADERS; ++i) + { + Shaders[i] = nullptr; + } + GammaShader = nullptr; + BlockSurface[0] = nullptr; + BlockSurface[1] = nullptr; + VSync = vid_vsync; + BlendingRect.left = 0; + BlendingRect.top = 0; + BlendingRect.right = FBWidth; + BlendingRect.bottom = FBHeight; + In2D = 0; + Palettes = nullptr; + Textures = nullptr; + Accel2D = true; + GatheringWipeScreen = false; + ScreenWipe = nullptr; + InScene = false; + QuadExtra = new BufferedTris[MAX_QUAD_BATCH]; + Atlases = nullptr; + PixelDoubling = 0; + SkipAt = -1; + CurrRenderTexture = 0; + RenderTextureToggle = 0; + + Gamma = 1.0; + FlashColor0 = 0; + FlashColor1 = 0xFFFFFFFF; + FlashColor = 0; + FlashAmount = 0; + + NeedGammaUpdate = false; + NeedPalUpdate = false; + + if (MemBuffer == nullptr) + { + return; + } + + memcpy(SourcePalette, GPalette.BaseColors, sizeof(PalEntry) * 256); + + Windowed = !(static_cast(Video)->GoFullscreen(fullscreen)); + + TrueHeight = height; + /*if (fullscreen) + { + for (Win32Video::ModeInfo *mode = static_cast(Video)->m_Modes; mode != nullptr; mode = mode->next) + { + if (mode->width == Width && mode->height == Height) + { + TrueHeight = mode->realheight; + PixelDoubling = mode->doubling; + break; + } + } + }*/ + // Offset from top of screen to top of letterboxed screen + LBOffsetI = (TrueHeight - Height) / 2; + LBOffset = float(LBOffsetI); + + CreateResources(); + SetInitialState(); +} + +OpenGLSWFrameBuffer::~OpenGLSWFrameBuffer() +{ + ReleaseResources(); + delete[] QuadExtra; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: SetInitialState +// +// Called after initial device creation and reset, when everything is set +// to OpenGL's defaults. +// +//========================================================================== + +void OpenGLSWFrameBuffer::SetInitialState() +{ + AlphaBlendEnabled = FALSE; + AlphaBlendOp = GL_FUNC_ADD; + AlphaSrcBlend = 0; + AlphaDestBlend = 0; + + CurPixelShader = nullptr; + memset(Constant, 0, sizeof(Constant)); + + for (unsigned i = 0; i < countof(Texture); ++i) + { + Texture[i] = nullptr; + SamplerWrapS[i] = GL_CLAMP_TO_EDGE; + SamplerWrapT[i] = GL_CLAMP_TO_EDGE; + } + + NeedGammaUpdate = true; + NeedPalUpdate = true; + OldRenderTarget = nullptr; + + // This constant is used for grayscaling weights (.xyz) and color inversion (.w) + float weights[4] = { 77 / 256.f, 143 / 256.f, 37 / 256.f, 1 }; + SetPixelShaderConstantF(PSCONST_Weights, weights, 1); + + AlphaTestEnabled = FALSE; + + CurBorderColor = 0; + + // Clear to black, just in case it wasn't done already. + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CreateResources +// +//========================================================================== + +bool OpenGLSWFrameBuffer::CreateResources() +{ + Atlases = nullptr; + if (!LoadShaders()) + { + return false; + } + if (!CreateFBTexture() || + !CreatePaletteTexture()) + { + return false; + } + if (!CreateVertexes()) + { + return false; + } + CreateGammaTexture(); + CreateBlockSurfaces(); + return true; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: LoadShaders +// +// Returns true if all required shaders were loaded. (Gamma and burn wipe +// are the only ones not considered "required".) +// +//========================================================================== + +bool OpenGLSWFrameBuffer::LoadShaders() +{ + static const char models[][4] = { "30/", "20/", "14/" }; + FString shaderdir, shaderpath; + unsigned model, i; + int lump; + + // We determine the best available model simply by trying them all in + // order of decreasing preference. + for (model = 0; model < countof(models); ++model) + { + shaderdir = "shaders/gl/sm"; + shaderdir += models[model]; + for (i = 0; i < NUM_SHADERS; ++i) + { + shaderpath = shaderdir; + shaderpath += ShaderNames[i]; + lump = Wads.CheckNumForFullName(shaderpath); + if (lump >= 0) + { + FMemLump data = Wads.ReadLump(lump); + if (!CreatePixelShader((uint32_t *)data.GetMem(), &Shaders[i]) && i < SHADER_BurnWipe) + { + break; + } + } + } + if (i == NUM_SHADERS) + { // Success! + return true; + } + // Failure. Release whatever managed to load (which is probably nothing.) + for (i = 0; i < NUM_SHADERS; ++i) + { + SafeRelease(Shaders[i]); + } + } + return false; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: ReleaseResources +// +//========================================================================== + +void OpenGLSWFrameBuffer::ReleaseResources() +{ + I_SaveWindowedPos(); + KillNativeTexs(); + KillNativePals(); + ReleaseDefaultPoolItems(); + SafeRelease(ScreenshotSurface); + SafeRelease(ScreenshotTexture); + SafeRelease(PaletteTexture); + for (int i = 0; i < NUM_SHADERS; ++i) + { + SafeRelease(Shaders[i]); + } + GammaShader = nullptr; + if (ScreenWipe != nullptr) + { + delete ScreenWipe; + ScreenWipe = nullptr; + } + Atlas *pack, *next; + for (pack = Atlases; pack != nullptr; pack = next) + { + next = pack->Next; + delete pack; + } + GatheringWipeScreen = false; +} + +void OpenGLSWFrameBuffer::ReleaseDefaultPoolItems() +{ + SafeRelease(FBTexture); + SafeRelease(FinalWipeScreen); + SafeRelease(RenderTexture[0]); + SafeRelease(RenderTexture[1]); + SafeRelease(InitialWipeScreen); + SafeRelease(VertexBuffer); + SafeRelease(IndexBuffer); + SafeRelease(BlockSurface[0]); + SafeRelease(BlockSurface[1]); + SafeRelease(FrontCopySurface); +} + +bool OpenGLSWFrameBuffer::Reset() +{ + ReleaseDefaultPoolItems(); + if (!CreateFBTexture() || !CreateVertexes()) + { + return false; + } + CreateBlockSurfaces(); + SetInitialState(); + return true; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CreateBlockSurfaces +// +// Create blocking surfaces for antilag. It's okay if these can't be +// created; antilag just won't work. +// +//========================================================================== + +void OpenGLSWFrameBuffer::CreateBlockSurfaces() +{ + BlockNum = 0; + if (CreateOffscreenPlainSurface(16, 16, GL_RGBA8, &BlockSurface[0])) + { + if (!CreateOffscreenPlainSurface(16, 16, GL_RGBA8, &BlockSurface[1])) + { + SafeRelease(BlockSurface[0]); + } + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: KillNativePals +// +// Frees all native palettes. +// +//========================================================================== + +void OpenGLSWFrameBuffer::KillNativePals() +{ + while (Palettes != nullptr) + { + delete Palettes; + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: KillNativeTexs +// +// Frees all native textures. +// +//========================================================================== + +void OpenGLSWFrameBuffer::KillNativeTexs() +{ + while (Textures != nullptr) + { + delete Textures; + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CreateFBTexture +// +// Creates the "Framebuffer" texture. With the advent of hardware-assisted +// 2D, this is something of a misnomer now. The FBTexture is only used for +// uploading the software 3D image to video memory so that it can be drawn +// to the real frame buffer. +// +// It also creates the TempRenderTexture, since this seemed like a +// convenient place to do so. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::CreateFBTexture() +{ + if (!CreateTexture(Width, Height, 1, GL_R8, &FBTexture)) + { + int pow2width, pow2height, i; + + for (i = 1; i < Width; i <<= 1) {} pow2width = i; + for (i = 1; i < Height; i <<= 1) {} pow2height = i; + + if (!CreateTexture(pow2width, pow2height, 1, GL_R8, &FBTexture)) + { + return false; + } + else + { + FBWidth = pow2width; + FBHeight = pow2height; + } + } + else + { + FBWidth = Width; + FBHeight = Height; + } + RenderTextureToggle = 0; + RenderTexture[0] = nullptr; + RenderTexture[1] = nullptr; + if (!CreateTexture(FBWidth, FBHeight, 1, GL_RGBA8, &RenderTexture[0])) + { + return false; + } + if (Windowed || PixelDoubling) + { + // Windowed or pixel doubling: Create another render texture so we can flip between them. + RenderTextureToggle = 1; + if (!CreateTexture(FBWidth, FBHeight, 1, GL_RGBA8, &RenderTexture[1])) + { + return false; + } + } + else + { + // Fullscreen and not pixel doubling: Create a render target to have the back buffer copied to. + if (!CreateRenderTarget(Width, Height, GL_RGBA8, &FrontCopySurface)) + { + return false; + } + } + // Initialize the TempRenderTextures to black. + for (int i = 0; i <= RenderTextureToggle; ++i) + { + HWSurface *surf; + if (RenderTexture[i]->GetSurfaceLevel(0, &surf)) + { + ColorFill(surf, 0.0f, 0.0f, 0.0f); + delete surf; + } + } + TempRenderTexture = RenderTexture[0]; + CurrRenderTexture = 0; + return true; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CreatePaletteTexture +// +//========================================================================== + +bool OpenGLSWFrameBuffer::CreatePaletteTexture() +{ + if (!CreateTexture(256, 1, 1, GL_RGBA8, &PaletteTexture)) + { + return false; + } + return true; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CreateGammaTexture +// +//========================================================================== + +bool OpenGLSWFrameBuffer::CreateGammaTexture() +{ + return false; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CreateVertexes +// +//========================================================================== + +bool OpenGLSWFrameBuffer::CreateVertexes() +{ + VertexPos = -1; + IndexPos = -1; + QuadBatchPos = -1; + BatchType = BATCH_None; + if (!CreateVertexBuffer(sizeof(FBVERTEX)*NUM_VERTS, &VertexBuffer)) + { + return false; + } + if (!CreateIndexBuffer(sizeof(uint16_t)*NUM_INDEXES, &IndexBuffer)) + { + return false; + } + return true; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CalcFullscreenCoords +// +//========================================================================== + +void OpenGLSWFrameBuffer::CalcFullscreenCoords(FBVERTEX verts[4], bool viewarea_only, bool can_double, uint32_t color0, uint32_t color1) const +{ + float offset = OldRenderTarget != nullptr ? 0 : LBOffset; + float top = offset - 0.5f; + float texright = float(Width) / float(FBWidth); + float texbot = float(Height) / float(FBHeight); + float mxl, mxr, myt, myb, tmxl, tmxr, tmyt, tmyb; + + if (viewarea_only) + { // Just calculate vertices for the viewarea/BlendingRect + mxl = float(BlendingRect.left) - 0.5f; + mxr = float(BlendingRect.right) - 0.5f; + myt = float(BlendingRect.top) + top; + myb = float(BlendingRect.bottom) + top; + tmxl = float(BlendingRect.left) / float(Width) * texright; + tmxr = float(BlendingRect.right) / float(Width) * texright; + tmyt = float(BlendingRect.top) / float(Height) * texbot; + tmyb = float(BlendingRect.bottom) / float(Height) * texbot; + } + else + { // Calculate vertices for the whole screen + mxl = -0.5f; + mxr = float(Width << (can_double ? PixelDoubling : 0)) - 0.5f; + myt = top; + myb = float(Height << (can_double ? PixelDoubling : 0)) + top; + tmxl = 0; + tmxr = texright; + tmyt = 0; + tmyb = texbot; + } + + //{ mxl, myt, 0, 1, 0, 0xFFFFFFFF, tmxl, tmyt }, + //{ mxr, myt, 0, 1, 0, 0xFFFFFFFF, tmxr, tmyt }, + //{ mxr, myb, 0, 1, 0, 0xFFFFFFFF, tmxr, tmyb }, + //{ mxl, myb, 0, 1, 0, 0xFFFFFFFF, tmxl, tmyb }, + + verts[0].x = mxl; + verts[0].y = myt; + verts[0].z = 0; + verts[0].rhw = 1; + verts[0].color0 = color0; + verts[0].color1 = color1; + verts[0].tu = tmxl; + verts[0].tv = tmyt; + + verts[1].x = mxr; + verts[1].y = myt; + verts[1].z = 0; + verts[1].rhw = 1; + verts[1].color0 = color0; + verts[1].color1 = color1; + verts[1].tu = tmxr; + verts[1].tv = tmyt; + + verts[2].x = mxr; + verts[2].y = myb; + verts[2].z = 0; + verts[2].rhw = 1; + verts[2].color0 = color0; + verts[2].color1 = color1; + verts[2].tu = tmxr; + verts[2].tv = tmyb; + + verts[3].x = mxl; + verts[3].y = myb; + verts[3].z = 0; + verts[3].rhw = 1; + verts[3].color0 = color0; + verts[3].color1 = color1; + verts[3].tu = tmxl; + verts[3].tv = tmyb; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: GetPageCount +// +//========================================================================== + +int OpenGLSWFrameBuffer::GetPageCount() +{ + return 1; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: PaletteChanged +// +//========================================================================== + +void OpenGLSWFrameBuffer::PaletteChanged() +{ +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: QueryNewPalette +// +//========================================================================== + +int OpenGLSWFrameBuffer::QueryNewPalette() +{ + return 0; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: IsValid +// +//========================================================================== + +bool OpenGLSWFrameBuffer::IsValid() +{ + return true; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: IsFullscreen +// +//========================================================================== + +bool OpenGLSWFrameBuffer::IsFullscreen() +{ + return !Windowed; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Lock +// +//========================================================================== + +bool OpenGLSWFrameBuffer::Lock(bool buffered) +{ + if (LockCount++ > 0) + { + return false; + } + assert(!In2D); + Accel2D = vid_hw2d; + Buffer = MemBuffer; + return false; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Unlock +// +//========================================================================== + +void OpenGLSWFrameBuffer::Unlock() +{ + LOG1("Unlock <%d>\n", LockCount); + + if (LockCount == 0) + { + return; + } + + if (UpdatePending && LockCount == 1) + { + Update(); + } + else if (--LockCount == 0) + { + Buffer = nullptr; + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Update +// +// When In2D == 0: Copy buffer to screen and present +// When In2D == 1: Copy buffer to screen but do not present +// When In2D == 2: Set up for 2D drawing but do not draw anything +// When In2D == 3: Present and set In2D to 0 +// +//========================================================================== + +void OpenGLSWFrameBuffer::Update() +{ + if (In2D == 3) + { + if (InScene) + { + DrawRateStuff(); + DrawPackedTextures(gl_showpacks); + EndBatch(); // Make sure all batched primitives are drawn. + Flip(); + } + In2D = 0; + return; + } + + if (LockCount != 1) + { + I_FatalError("Framebuffer must have exactly 1 lock to be updated"); + if (LockCount > 0) + { + UpdatePending = true; + --LockCount; + } + return; + } + + if (In2D == 0) + { + DrawRateStuff(); + } + + if (NeedGammaUpdate) + { + float psgamma[4]; + float igamma; + + NeedGammaUpdate = false; + igamma = 1 / Gamma; + if (!Windowed) + { + GammaRamp ramp; + + for (int i = 0; i < 256; ++i) + { + ramp.blue[i] = ramp.green[i] = ramp.red[i] = uint16_t(65535.f * powf(i / 255.f, igamma)); + } + LOG("SetGammaRamp\n"); + SetGammaRamp(&ramp); + } + else + { + if (igamma != 1) + { + UpdateGammaTexture(igamma); + GammaShader = Shaders[SHADER_GammaCorrection]; + } + else + { + GammaShader = nullptr; + } + } + psgamma[2] = psgamma[1] = psgamma[0] = igamma; + psgamma[3] = 0.5; // For SM14 version + SetPixelShaderConstantF(PSCONST_Gamma, psgamma, 1); + } + + if (NeedPalUpdate) + { + UploadPalette(); + } + + BlitCycles.Reset(); + BlitCycles.Clock(); + + LockCount = 0; + Draw3DPart(In2D <= 1); + if (In2D == 0) + { + Flip(); + } + + BlitCycles.Unclock(); + //LOG1 ("cycles = %d\n", BlitCycles); + + Buffer = nullptr; + UpdatePending = false; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Flip +// +//========================================================================== + +void OpenGLSWFrameBuffer::Flip() +{ + assert(InScene); + + DrawLetterbox(); + DoWindowedGamma(); + + CopyNextFrontBuffer(); + + // Attempt to counter input lag. + if (gl_antilag && BlockSurface[0] != nullptr) + { + LockedRect lr; + volatile int dummy; + ColorFill(BlockSurface[BlockNum], 0.0f, 0x20/255.0f, 0x50/255.0f); + BlockNum ^= 1; + if (BlockSurface[BlockNum]->LockRect(&lr, nullptr, false)) + { + dummy = *(int *)lr.pBits; + BlockSurface[BlockNum]->UnlockRect(); + } + } + // Limiting the frame rate is as simple as waiting for the timer to signal this event. + if (FPSLimitEvent != nullptr) + { + WaitForSingleObject(FPSLimitEvent, 1000); + } + Present(); + InScene = false; + + if (RenderTextureToggle) + { + // Flip the TempRenderTexture to the other one now. + CurrRenderTexture ^= RenderTextureToggle; + TempRenderTexture = RenderTexture[CurrRenderTexture]; + } + + if (Windowed) + { + int clientWidth = GetClientWidth(); + int clientHeight = GetClientHeight(); + if (clientWidth > 0 && clientHeight > 0 && (Width != clientWidth || Height != clientHeight)) + { + Resize(clientWidth, clientHeight); + + TrueHeight = Height; + PixelDoubling = 0; + LBOffsetI = 0; + LBOffset = 0.0f; + Reset(); + + V_OutputResized(Width, Height); + } + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CopyNextFrontBuffer +// +// Duplicates the contents of the back buffer that will become the front +// buffer upon Present into FrontCopySurface so that we can get the +// contents of the display without wasting time in GetFrontBufferData(). +// +//========================================================================== + +void OpenGLSWFrameBuffer::CopyNextFrontBuffer() +{ + HWSurface *backbuff; + + if (Windowed || PixelDoubling) + { + // Windowed mode or pixel doubling: TempRenderTexture has what we want + SafeRelease(FrontCopySurface); + if (TempRenderTexture->GetSurfaceLevel(0, &backbuff)) + { + FrontCopySurface = backbuff; + } + } + else + { + // Fullscreen, not pixel doubled: The back buffer has what we want, + // but it might be letter boxed. + if (GetBackBuffer(&backbuff)) + { + LTRBRect srcrect = { 0, LBOffsetI, Width, LBOffsetI + Height }; + StretchRect(backbuff, &srcrect, FrontCopySurface); + delete backbuff; + } + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: PaintToWindow +// +//========================================================================== + +bool OpenGLSWFrameBuffer::PaintToWindow() +{ + if (LockCount != 0) + { + return false; + } + Draw3DPart(true); + return true; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Draw3DPart +// +// The software 3D part, to be exact. +// +//========================================================================== + +void OpenGLSWFrameBuffer::Draw3DPart(bool copy3d) +{ + if (copy3d) + { + LTRBRect texrect = { 0, 0, Width, Height }; + LockedRect lockrect; + + if ((FBWidth == Width && FBHeight == Height && + FBTexture->LockRect(&lockrect, nullptr, true)) || + FBTexture->LockRect(&lockrect, &texrect, false)) + { + if (lockrect.Pitch == Pitch && Pitch == Width) + { + memcpy(lockrect.pBits, MemBuffer, Width * Height); + } + else + { + uint8_t *dest = (uint8_t *)lockrect.pBits; + uint8_t *src = MemBuffer; + for (int y = 0; y < Height; y++) + { + memcpy(dest, src, Width); + dest += lockrect.Pitch; + src += Pitch; + } + } + FBTexture->UnlockRect(); + } + } + InScene = true; + if (vid_hwaalines) + glEnable(GL_LINE_SMOOTH); + else + glDisable(GL_LINE_SMOOTH); + assert(OldRenderTarget == nullptr); + if (TempRenderTexture != nullptr && + ((Windowed && TempRenderTexture != FinalWipeScreen) || GatheringWipeScreen || PixelDoubling)) + { + HWSurface *targetsurf; + if (TempRenderTexture->GetSurfaceLevel(0, &targetsurf)) + { + if (GetRenderTarget(0, &OldRenderTarget)) + { + SetRenderTarget(0, targetsurf); + } + delete targetsurf; + } + } + + SetTexture(0, FBTexture); + SetPaletteTexture(PaletteTexture, 256, BorderColor); + memset(Constant, 0, sizeof(Constant)); + SetAlphaBlend(0); + EnableAlphaTest(FALSE); + SetPixelShader(Shaders[SHADER_NormalColorPal]); + if (copy3d) + { + FBVERTEX verts[4]; + uint32_t color0, color1; + if (Accel2D) + { + if (realfixedcolormap == nullptr) + { + color0 = 0; + color1 = 0xFFFFFFF; + } + else + { + color0 = ColorValue(realfixedcolormap->ColorizeStart[0] / 2, + realfixedcolormap->ColorizeStart[1] / 2, realfixedcolormap->ColorizeStart[2] / 2, 0); + color1 = ColorValue(realfixedcolormap->ColorizeEnd[0] / 2, + realfixedcolormap->ColorizeEnd[1] / 2, realfixedcolormap->ColorizeEnd[2] / 2, 1); + SetPixelShader(Shaders[SHADER_SpecialColormapPal]); + } + } + else + { + color0 = FlashColor0; + color1 = FlashColor1; + } + CalcFullscreenCoords(verts, Accel2D, false, color0, color1); + DrawTriangleFans(2, verts); + } + SetPixelShader(Shaders[SHADER_NormalColorPal]); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: DrawLetterbox +// +// Draws the black bars at the top and bottom of the screen for letterboxed +// modes. +// +//========================================================================== + +void OpenGLSWFrameBuffer::DrawLetterbox() +{ + if (LBOffsetI != 0) + { + glEnable(GL_SCISSOR_TEST); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glScissor(0, 0, Width, LBOffsetI); + glClear(GL_COLOR_BUFFER_BIT); + glScissor(0, Height + LBOffsetI, Width, TrueHeight - Height + LBOffsetI); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_SCISSOR_TEST); + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: DoWindowedGamma +// +// Draws the render target texture to the real back buffer using a gamma- +// correcting pixel shader. +// +//========================================================================== + +void OpenGLSWFrameBuffer::DoWindowedGamma() +{ + if (OldRenderTarget != nullptr) + { + FBVERTEX verts[4]; + + CalcFullscreenCoords(verts, false, true, 0, 0xFFFFFFFF); + SetRenderTarget(0, OldRenderTarget); + SetTexture(0, TempRenderTexture); + SetPixelShader(Windowed && GammaShader ? GammaShader : Shaders[SHADER_NormalColor]); + SetAlphaBlend(0); + EnableAlphaTest(FALSE); + DrawTriangleFans(2, verts); + delete OldRenderTarget; + OldRenderTarget = nullptr; + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: UpdateGammaTexture +// +// Updates the gamma texture used by the PS14 shader. We only use the first +// half of the texture so that we needn't worry about imprecision causing +// it to grab from the border. +// +//========================================================================== + +void OpenGLSWFrameBuffer::UpdateGammaTexture(float igamma) +{ + LockedRect lockrect; + + if (GammaTexture != nullptr && GammaTexture->LockRect(&lockrect, nullptr, false)) + { + uint8_t *pix = (uint8_t *)lockrect.pBits; + for (int i = 0; i <= 128; ++i) + { + pix[i * 4 + 2] = pix[i * 4 + 1] = pix[i * 4] = uint8_t(255.f * powf(i / 128.f, igamma)); + pix[i * 4 + 3] = 255; + } + GammaTexture->UnlockRect(); + } +} + +void OpenGLSWFrameBuffer::UploadPalette() +{ + LockedRect lockrect; + + if (SkipAt < 0) + { + SkipAt = 256; + } + if (PaletteTexture->LockRect(&lockrect, nullptr, false)) + { + uint8_t *pix = (uint8_t *)lockrect.pBits; + int i; + + for (i = 0; i < SkipAt; ++i, pix += 4) + { + pix[0] = SourcePalette[i].b; + pix[1] = SourcePalette[i].g; + pix[2] = SourcePalette[i].r; + pix[3] = (i == 0 ? 0 : 255); + // To let masked textures work, the first palette entry's alpha is 0. + } + pix += 4; + for (; i < 255; ++i, pix += 4) + { + pix[0] = SourcePalette[i].b; + pix[1] = SourcePalette[i].g; + pix[2] = SourcePalette[i].r; + pix[3] = 255; + } + PaletteTexture->UnlockRect(); + BorderColor = ColorXRGB(SourcePalette[255].r, SourcePalette[255].g, SourcePalette[255].b); + } +} + +PalEntry *OpenGLSWFrameBuffer::GetPalette() +{ + return SourcePalette; +} + +void OpenGLSWFrameBuffer::UpdatePalette() +{ + NeedPalUpdate = true; +} + +bool OpenGLSWFrameBuffer::SetGamma(float gamma) +{ + LOG1("SetGamma %g\n", gamma); + Gamma = gamma; + NeedGammaUpdate = true; + return true; +} + +bool OpenGLSWFrameBuffer::SetFlash(PalEntry rgb, int amount) +{ + FlashColor = rgb; + FlashAmount = amount; + + // Fill in the constants for the pixel shader to do linear interpolation between the palette and the flash: + float r = rgb.r / 255.f, g = rgb.g / 255.f, b = rgb.b / 255.f, a = amount / 256.f; + FlashColor0 = ColorValue(r * a, g * a, b * a, 0); + a = 1 - a; + FlashColor1 = ColorValue(a, a, a, 1); + return true; +} + +void OpenGLSWFrameBuffer::GetFlash(PalEntry &rgb, int &amount) +{ + rgb = FlashColor; + amount = FlashAmount; +} + +void OpenGLSWFrameBuffer::GetFlashedPalette(PalEntry pal[256]) +{ + memcpy(pal, SourcePalette, 256 * sizeof(PalEntry)); + if (FlashAmount) + { + DoBlending(pal, pal, 256, FlashColor.r, FlashColor.g, FlashColor.b, FlashAmount); + } +} + +void OpenGLSWFrameBuffer::SetVSync(bool vsync) +{ + if (VSync != vsync) + { + VSync = vsync; + Reset(); + } +} + +void OpenGLSWFrameBuffer::NewRefreshRate() +{ + if (!Windowed) + { + Reset(); + } +} + +void OpenGLSWFrameBuffer::Blank() +{ +} + +void OpenGLSWFrameBuffer::SetBlendingRect(int x1, int y1, int x2, int y2) +{ + BlendingRect.left = x1; + BlendingRect.top = y1; + BlendingRect.right = x2; + BlendingRect.bottom = y2; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: GetScreenshotBuffer +// +// Returns a pointer into a surface holding the current screen data. +// +//========================================================================== + +void OpenGLSWFrameBuffer::GetScreenshotBuffer(const uint8_t *&buffer, int &pitch, ESSType &color_type) +{ + LockedRect lrect; + + if (!Accel2D) + { + Super::GetScreenshotBuffer(buffer, pitch, color_type); + return; + } + buffer = nullptr; + if ((ScreenshotTexture = GetCurrentScreen()) != nullptr) + { + if (!ScreenshotTexture->GetSurfaceLevel(0, &ScreenshotSurface)) + { + delete ScreenshotTexture; + ScreenshotTexture = nullptr; + } + else if (!ScreenshotSurface->LockRect(&lrect, nullptr, false)) + { + delete ScreenshotSurface; + ScreenshotSurface = nullptr; + delete ScreenshotTexture; + ScreenshotTexture = nullptr; + } + else + { + buffer = (const uint8_t *)lrect.pBits; + pitch = lrect.Pitch; + color_type = SS_BGRA; + } + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: ReleaseScreenshotBuffer +// +//========================================================================== + +void OpenGLSWFrameBuffer::ReleaseScreenshotBuffer() +{ + if (LockCount > 0) + { + Super::ReleaseScreenshotBuffer(); + } + if (ScreenshotSurface != nullptr) + { + ScreenshotSurface->UnlockRect(); + delete ScreenshotSurface; + ScreenshotSurface = nullptr; + } + SafeRelease(ScreenshotTexture); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: GetCurrentScreen +// +// Returns a texture containing the pixels currently visible on-screen. +// +//========================================================================== + +OpenGLSWFrameBuffer::HWTexture *OpenGLSWFrameBuffer::GetCurrentScreen() +{ + HWTexture *tex; + HWSurface *surf; + bool hr; + + if (FrontCopySurface == nullptr) + { + return nullptr; + } + + hr = CreateTexture(FBWidth, FBHeight, 1, GL_RGBA8, &tex); + + if (!hr) + { + return nullptr; + } + if (!tex->GetSurfaceLevel(0, &surf)) + { + delete tex; + return nullptr; + } + + // Video -> System memory : use GetRenderTargetData + GetRenderTargetData(FrontCopySurface, surf); + delete surf; + + if (!hr) + { + delete tex; + return nullptr; + } + return tex; +} + +/**************************************************************************/ +/* 2D Stuff */ +/**************************************************************************/ + +//========================================================================== +// +// OpenGLSWFrameBuffer :: DrawPackedTextures +// +// DEBUG: Draws the texture atlases to the screen, starting with the +// 1-based packnum. Ignores atlases that are flagged for use by one +// texture only. +// +//========================================================================== + +void OpenGLSWFrameBuffer::DrawPackedTextures(int packnum) +{ + uint32_t empty_colors[8] = + { + 0x50FF0000, 0x5000FF00, 0x500000FF, 0x50FFFF00, + 0x50FF00FF, 0x5000FFFF, 0x50FF8000, 0x500080FF + }; + Atlas *pack; + int x = 8, y = 8; + + if (packnum <= 0) + { + return; + } + pack = Atlases; + // Find the first texture atlas that is an actual atlas. + while (pack != nullptr && pack->OneUse) + { // Skip textures that aren't used as atlases + pack = pack->Next; + } + // Skip however many atlases we would have otherwise drawn + // until we've skipped of them. + while (pack != nullptr && packnum != 1) + { + if (!pack->OneUse) + { // Skip textures that aren't used as atlases + packnum--; + } + pack = pack->Next; + } + // Draw atlases until we run out of room on the screen. + while (pack != nullptr) + { + if (pack->OneUse) + { // Skip textures that aren't used as atlases + pack = pack->Next; + continue; + } + + AddColorOnlyRect(x - 1, y - 1 - LBOffsetI, 258, 258, ColorXRGB(255, 255, 0)); + int back = 0; + for (PackedTexture *box = pack->UsedList; box != nullptr; box = box->Next) + { + AddColorOnlyQuad( + x + box->Area.left * 256 / pack->Width, + y + box->Area.top * 256 / pack->Height, + (box->Area.right - box->Area.left) * 256 / pack->Width, + (box->Area.bottom - box->Area.top) * 256 / pack->Height, empty_colors[back]); + back = (back + 1) & 7; + } + // AddColorOnlyQuad(x, y-LBOffsetI, 256, 256, ColorARGB(180,0,0,0)); + + CheckQuadBatch(); + + BufferedTris *quad = &QuadExtra[QuadBatchPos]; + FBVERTEX *vert = &VertexData[VertexPos]; + + quad->Group1 = 0; + if (pack->Format == GL_R8/* && !tex->IsGray*/) + { + quad->Flags = BQF_WrapUV | BQF_GamePalette/* | BQF_DisableAlphaTest*/; + quad->ShaderNum = BQS_PalTex; + } + else + { + quad->Flags = BQF_WrapUV/* | BQF_DisableAlphaTest*/; + quad->ShaderNum = BQS_Plain; + } + quad->Palette = nullptr; + quad->Texture = pack->Tex; + quad->NumVerts = 4; + quad->NumTris = 2; + + float x0 = float(x) - 0.5f; + float y0 = float(y) - 0.5f; + float x1 = x0 + 256.f; + float y1 = y0 + 256.f; + + vert[0].x = x0; + vert[0].y = y0; + vert[0].z = 0; + vert[0].rhw = 1; + vert[0].color0 = 0; + vert[0].color1 = 0xFFFFFFFF; + vert[0].tu = 0; + vert[0].tv = 0; + + vert[1].x = x1; + vert[1].y = y0; + vert[1].z = 0; + vert[1].rhw = 1; + vert[1].color0 = 0; + vert[1].color1 = 0xFFFFFFFF; + vert[1].tu = 1; + vert[1].tv = 0; + + vert[2].x = x1; + vert[2].y = y1; + vert[2].z = 0; + vert[2].rhw = 1; + vert[2].color0 = 0; + vert[2].color1 = 0xFFFFFFFF; + vert[2].tu = 1; + vert[2].tv = 1; + + vert[3].x = x0; + vert[3].y = y1; + vert[3].z = 0; + vert[3].rhw = 1; + vert[3].color0 = 0; + vert[3].color1 = 0xFFFFFFFF; + vert[3].tu = 0; + vert[3].tv = 1; + + IndexData[IndexPos] = VertexPos; + IndexData[IndexPos + 1] = VertexPos + 1; + IndexData[IndexPos + 2] = VertexPos + 2; + IndexData[IndexPos + 3] = VertexPos; + IndexData[IndexPos + 4] = VertexPos + 2; + IndexData[IndexPos + 5] = VertexPos + 3; + + QuadBatchPos++; + VertexPos += 4; + IndexPos += 6; + + x += 256 + 8; + if (x > Width - 256) + { + x = 8; + y += 256 + 8; + if (y > TrueHeight - 256) + { + return; + } + } + pack = pack->Next; + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: AllocPackedTexture +// +// Finds space to pack an image inside a texture atlas and returns it. +// Large images and those that need to wrap always get their own textures. +// +//========================================================================== + +OpenGLSWFrameBuffer::PackedTexture *OpenGLSWFrameBuffer::AllocPackedTexture(int w, int h, bool wrapping, int format) +{ + Atlas *pack; + Rect box; + bool padded; + + // The - 2 to account for padding + if (w > 256 - 2 || h > 256 - 2 || wrapping) + { // Create a new texture atlas. + pack = new Atlas(this, w, h, format); + pack->OneUse = true; + box = pack->Packer.Insert(w, h); + padded = false; + } + else + { // Try to find space in an existing texture atlas. + w += 2; // Add padding + h += 2; + for (pack = Atlases; pack != nullptr; pack = pack->Next) + { + // Use the first atlas it fits in. + if (pack->Format == format) + { + box = pack->Packer.Insert(w, h); + if (box.width != 0) + { + break; + } + } + } + if (pack == nullptr) + { // Create a new texture atlas. + pack = new Atlas(this, DEF_ATLAS_WIDTH, DEF_ATLAS_HEIGHT, format); + box = pack->Packer.Insert(w, h); + } + padded = true; + } + assert(box.width != 0 && box.height != 0); + return pack->AllocateImage(box, padded); +} + +//========================================================================== +// +// Atlas Constructor +// +//========================================================================== + +OpenGLSWFrameBuffer::Atlas::Atlas(OpenGLSWFrameBuffer *fb, int w, int h, int format) + : Packer(w, h, true) +{ + Tex = nullptr; + Format = format; + UsedList = nullptr; + OneUse = false; + Width = 0; + Height = 0; + Next = nullptr; + + // Attach to the end of the atlas list + Atlas **prev = &fb->Atlases; + while (*prev != nullptr) + { + prev = &((*prev)->Next); + } + *prev = this; + + fb->CreateTexture(w, h, 1, format, &Tex); + Width = w; + Height = h; +} + +//========================================================================== +// +// Atlas Destructor +// +//========================================================================== + +OpenGLSWFrameBuffer::Atlas::~Atlas() +{ + PackedTexture *box, *next; + + SafeRelease(Tex); + for (box = UsedList; box != nullptr; box = next) + { + next = box->Next; + delete box; + } +} + +//========================================================================== +// +// Atlas :: AllocateImage +// +// Moves the box from the empty list to the used list, sizing it to the +// requested dimensions and adding additional boxes to the empty list if +// needed. +// +// The passed box *MUST* be in this texture atlas's empty list. +// +//========================================================================== + +OpenGLSWFrameBuffer::PackedTexture *OpenGLSWFrameBuffer::Atlas::AllocateImage(const Rect &rect, bool padded) +{ + PackedTexture *box = new PackedTexture; + + box->Owner = this; + box->Area.left = rect.x; + box->Area.top = rect.y; + box->Area.right = rect.x + rect.width; + box->Area.bottom = rect.y + rect.height; + + box->Left = float(box->Area.left + padded) / Width; + box->Right = float(box->Area.right - padded) / Width; + box->Top = float(box->Area.top + padded) / Height; + box->Bottom = float(box->Area.bottom - padded) / Height; + + box->Padded = padded; + + // Add it to the used list. + box->Next = UsedList; + if (box->Next != nullptr) + { + box->Next->Prev = &box->Next; + } + UsedList = box; + box->Prev = &UsedList; + + return box; +} + +//========================================================================== +// +// Atlas :: FreeBox +// +// Removes a box from the used list and deletes it. Space is returned to the +// waste list. Once all boxes for this atlas are freed, the entire bin +// packer is reinitialized for maximum efficiency. +// +//========================================================================== + +void OpenGLSWFrameBuffer::Atlas::FreeBox(OpenGLSWFrameBuffer::PackedTexture *box) +{ + *(box->Prev) = box->Next; + if (box->Next != nullptr) + { + box->Next->Prev = box->Prev; + } + Rect waste; + waste.x = box->Area.left; + waste.y = box->Area.top; + waste.width = box->Area.right - box->Area.left; + waste.height = box->Area.bottom - box->Area.top; + box->Owner->Packer.AddWaste(waste); + delete box; + if (UsedList == nullptr) + { + Packer.Init(Width, Height, true); + } +} + +//========================================================================== +// +// OpenGLTex Constructor +// +//========================================================================== + +OpenGLSWFrameBuffer::OpenGLTex::OpenGLTex(FTexture *tex, OpenGLSWFrameBuffer *fb, bool wrapping) +{ + // Attach to the texture list for the OpenGLSWFrameBuffer + Next = fb->Textures; + if (Next != nullptr) + { + Next->Prev = &Next; + } + Prev = &fb->Textures; + fb->Textures = this; + + GameTex = tex; + Box = nullptr; + IsGray = false; + + Create(fb, wrapping); +} + +//========================================================================== +// +// OpenGLTex Destructor +// +//========================================================================== + +OpenGLSWFrameBuffer::OpenGLTex::~OpenGLTex() +{ + if (Box != nullptr) + { + Box->Owner->FreeBox(Box); + Box = nullptr; + } + // Detach from the texture list + *Prev = Next; + if (Next != nullptr) + { + Next->Prev = Prev; + } + // Remove link from the game texture + if (GameTex != nullptr) + { + GameTex->Native = nullptr; + } +} + +//========================================================================== +// +// OpenGLTex :: CheckWrapping +// +// Returns true if the texture is compatible with the specified wrapping +// mode. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::OpenGLTex::CheckWrapping(bool wrapping) +{ + // If it doesn't need to wrap, then it works. + if (!wrapping) + { + return true; + } + // If it needs to wrap, then it can't be packed inside another texture. + return Box->Owner->OneUse; +} + +//========================================================================== +// +// OpenGLTex :: Create +// +// Creates an HWTexture for the texture and copies the image data +// to it. Note that unlike FTexture, this image is row-major. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::OpenGLTex::Create(OpenGLSWFrameBuffer *fb, bool wrapping) +{ + assert(Box == nullptr); + if (Box != nullptr) + { + Box->Owner->FreeBox(Box); + } + + Box = fb->AllocPackedTexture(GameTex->GetWidth(), GameTex->GetHeight(), wrapping, GetTexFormat()); + + if (Box == nullptr) + { + return false; + } + if (!Update()) + { + Box->Owner->FreeBox(Box); + Box = nullptr; + return false; + } + return true; +} + +//========================================================================== +// +// OpenGLTex :: Update +// +// Copies image data from the underlying FTexture to the OpenGL texture. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::OpenGLTex::Update() +{ + LockedRect lrect; + LTRBRect rect; + uint8_t *dest; + + assert(Box != nullptr); + assert(Box->Owner != nullptr); + assert(Box->Owner->Tex != nullptr); + assert(GameTex != nullptr); + + int format = Box->Owner->Tex->Format; + + rect = Box->Area; + if (!Box->Owner->Tex->LockRect(&lrect, &rect, false)) + { + return false; + } + dest = (uint8_t *)lrect.pBits; + if (Box->Padded) + { + dest += lrect.Pitch + (format == GL_R8 ? 1 : 4); + } + GameTex->FillBuffer(dest, lrect.Pitch, GameTex->GetHeight(), ToTexFmt(format)); + if (Box->Padded) + { + // Clear top padding row. + dest = (uint8_t *)lrect.pBits; + int numbytes = GameTex->GetWidth() + 2; + if (format != GL_R8) + { + numbytes <<= 2; + } + memset(dest, 0, numbytes); + dest += lrect.Pitch; + // Clear left and right padding columns. + if (format == GL_R8) + { + for (int y = Box->Area.bottom - Box->Area.top - 2; y > 0; --y) + { + dest[0] = 0; + dest[numbytes - 1] = 0; + dest += lrect.Pitch; + } + } + else + { + for (int y = Box->Area.bottom - Box->Area.top - 2; y > 0; --y) + { + *(uint32_t *)dest = 0; + *(uint32_t *)(dest + numbytes - 4) = 0; + dest += lrect.Pitch; + } + } + // Clear bottom padding row. + memset(dest, 0, numbytes); + } + Box->Owner->Tex->UnlockRect(); + return true; +} + +//========================================================================== +// +// OpenGLTex :: GetTexFormat +// +// Returns the texture format that would best fit this texture. +// +//========================================================================== + +int OpenGLSWFrameBuffer::OpenGLTex::GetTexFormat() +{ + FTextureFormat fmt = GameTex->GetFormat(); + + IsGray = false; + + switch (fmt) + { + case TEX_Pal: return GL_R8; + case TEX_Gray: IsGray = true; return GL_R8; + case TEX_RGB: return GL_RGBA8; + case TEX_DXT1: return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; + case TEX_DXT2: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case TEX_DXT3: return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case TEX_DXT4: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; // Doesn't exist in OpenGL. Closest match is DXT5. + case TEX_DXT5: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + default: I_FatalError("GameTex->GetFormat() returned invalid format."); + } + return GL_R8; +} + +//========================================================================== +// +// OpenGLTex :: ToTexFmt +// +// Converts an OpenGL internal format constant to something the FTexture system +// understands. +// +//========================================================================== + +FTextureFormat OpenGLSWFrameBuffer::OpenGLTex::ToTexFmt(int fmt) +{ + switch (fmt) + { + case GL_R8: return IsGray ? TEX_Gray : TEX_Pal; + case GL_RGBA8: return TEX_RGB; + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: return TEX_DXT1; + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: return TEX_DXT2; + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: return TEX_DXT3; + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: return TEX_DXT5; + default: + assert(0); // LOL WUT? + return TEX_Pal; + } +} + +//========================================================================== +// +// OpenGLPal Constructor +// +//========================================================================== + +OpenGLSWFrameBuffer::OpenGLPal::OpenGLPal(FRemapTable *remap, OpenGLSWFrameBuffer *fb) + : Tex(nullptr), Remap(remap) +{ + int count; + + // Attach to the palette list for the OpenGLSWFrameBuffer + Next = fb->Palettes; + if (Next != nullptr) + { + Next->Prev = &Next; + } + Prev = &fb->Palettes; + fb->Palettes = this; + + int pow2count; + + // Round up to the nearest power of 2. + for (pow2count = 1; pow2count < remap->NumEntries; pow2count <<= 1) + { + } + count = pow2count; + DoColorSkip = false; + + BorderColor = 0; + RoundedPaletteSize = count; + if (fb->CreateTexture(count, 1, 1, GL_RGBA8, &Tex)) + { + if (!Update()) + { + delete Tex; + Tex = nullptr; + } + } +} + +//========================================================================== +// +// OpenGLPal Destructor +// +//========================================================================== + +OpenGLSWFrameBuffer::OpenGLPal::~OpenGLPal() +{ + SafeRelease(Tex); + // Detach from the palette list + *Prev = Next; + if (Next != nullptr) + { + Next->Prev = Prev; + } + // Remove link from the remap table + if (Remap != nullptr) + { + Remap->Native = nullptr; + } +} + +//========================================================================== +// +// OpenGLPal :: Update +// +// Copies the palette to the texture. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::OpenGLPal::Update() +{ + LockedRect lrect; + uint32_t *buff; + const PalEntry *pal; + int skipat, i; + + assert(Tex != nullptr); + + if (!Tex->LockRect(&lrect, nullptr, 0)) + { + return false; + } + buff = (uint32_t *)lrect.pBits; + pal = Remap->Palette; + + // See explanation in UploadPalette() for skipat rationale. + skipat = MIN(Remap->NumEntries, DoColorSkip ? 256 - 8 : 256); + + for (i = 0; i < skipat; ++i) + { + buff[i] = ColorARGB(pal[i].a, pal[i].r, pal[i].g, pal[i].b); + } + for (++i; i < Remap->NumEntries; ++i) + { + buff[i] = ColorARGB(pal[i].a, pal[i - 1].r, pal[i - 1].g, pal[i - 1].b); + } + BorderColor = ColorARGB(pal[i].a, pal[i - 1].r, pal[i - 1].g, pal[i - 1].b); + + Tex->UnlockRect(); + return true; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Begin2D +// +// Begins 2D mode drawing operations. In particular, DrawTexture is +// rerouted to use Direct3D instead of the software renderer. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::Begin2D(bool copy3d) +{ + if (!Accel2D) + { + return false; + } + if (In2D) + { + return true; + } + In2D = 2 - copy3d; + Update(); + In2D = 3; + + return true; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: DrawBlendingRect +// +// Call after Begin2D to blend the 3D view. +// +//========================================================================== + +void OpenGLSWFrameBuffer::DrawBlendingRect() +{ + if (!In2D || !Accel2D) + { + return; + } + Dim(FlashColor, FlashAmount / 256.f, viewwindowx, viewwindowy, viewwidth, viewheight); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CreateTexture +// +// Returns a native texture that wraps a FTexture. +// +//========================================================================== + +FNativeTexture *OpenGLSWFrameBuffer::CreateTexture(FTexture *gametex, bool wrapping) +{ + OpenGLTex *tex = new OpenGLTex(gametex, this, wrapping); + if (tex->Box == nullptr) + { + delete tex; + return nullptr; + } + return tex; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CreatePalette +// +// Returns a native texture that contains a palette. +// +//========================================================================== + +FNativePalette *OpenGLSWFrameBuffer::CreatePalette(FRemapTable *remap) +{ + OpenGLPal *tex = new OpenGLPal(remap, this); + if (tex->Tex == nullptr) + { + delete tex; + return nullptr; + } + return tex; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Clear +// +// Fills the specified region with a color. +// +//========================================================================== + +void OpenGLSWFrameBuffer::Clear(int left, int top, int right, int bottom, int palcolor, uint32 color) +{ + if (In2D < 2) + { + Super::Clear(left, top, right, bottom, palcolor, color); + return; + } + if (!InScene) + { + return; + } + if (palcolor >= 0 && color == 0) + { + color = GPalette.BaseColors[palcolor]; + } + else if (APART(color) < 255) + { + Dim(color, APART(color) / 255.f, left, top, right - left, bottom - top); + return; + } + AddColorOnlyQuad(left, top, right - left, bottom - top, color | 0xFF000000); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Dim +// +//========================================================================== + +void OpenGLSWFrameBuffer::Dim(PalEntry color, float amount, int x1, int y1, int w, int h) +{ + if (amount <= 0) + { + return; + } + if (In2D < 2) + { + Super::Dim(color, amount, x1, y1, w, h); + return; + } + if (!InScene) + { + return; + } + if (amount > 1) + { + amount = 1; + } + AddColorOnlyQuad(x1, y1, w, h, color | (int(amount * 255) << 24)); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: BeginLineBatch +// +//========================================================================== + +void OpenGLSWFrameBuffer::BeginLineBatch() +{ + if (In2D < 2 || !InScene || BatchType == BATCH_Lines) + { + return; + } + EndQuadBatch(); // Make sure all quads have been drawn first. + VertexData = VertexBuffer->Lock(); + VertexPos = 0; + BatchType = BATCH_Lines; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: EndLineBatch +// +//========================================================================== + +void OpenGLSWFrameBuffer::EndLineBatch() +{ + if (In2D < 2 || !InScene || BatchType != BATCH_Lines) + { + return; + } + VertexBuffer->Unlock(); + if (VertexPos > 0) + { + SetPixelShader(Shaders[SHADER_VertexColor]); + SetAlphaBlend(GL_FUNC_ADD, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + SetStreamSource(VertexBuffer); + DrawLineList(VertexPos / 2); + } + VertexPos = -1; + BatchType = BATCH_None; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: DrawLine +// +//========================================================================== + +void OpenGLSWFrameBuffer::DrawLine(int x0, int y0, int x1, int y1, int palcolor, uint32 color) +{ + if (In2D < 2) + { + Super::DrawLine(x0, y0, x1, y1, palcolor, color); + return; + } + if (!InScene) + { + return; + } + if (BatchType != BATCH_Lines) + { + BeginLineBatch(); + } + if (VertexPos == NUM_VERTS) + { // Flush the buffer and refill it. + EndLineBatch(); + BeginLineBatch(); + } + // Add the endpoints to the vertex buffer. + VertexData[VertexPos].x = float(x0); + VertexData[VertexPos].y = float(y0) + LBOffset; + VertexData[VertexPos].z = 0; + VertexData[VertexPos].rhw = 1; + VertexData[VertexPos].color0 = color; + VertexData[VertexPos].color1 = 0; + VertexData[VertexPos].tu = 0; + VertexData[VertexPos].tv = 0; + + VertexData[VertexPos + 1].x = float(x1); + VertexData[VertexPos + 1].y = float(y1) + LBOffset; + VertexData[VertexPos + 1].z = 0; + VertexData[VertexPos + 1].rhw = 1; + VertexData[VertexPos + 1].color0 = color; + VertexData[VertexPos + 1].color1 = 0; + VertexData[VertexPos + 1].tu = 0; + VertexData[VertexPos + 1].tv = 0; + + VertexPos += 2; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: DrawPixel +// +//========================================================================== + +void OpenGLSWFrameBuffer::DrawPixel(int x, int y, int palcolor, uint32 color) +{ + if (In2D < 2) + { + Super::DrawPixel(x, y, palcolor, color); + return; + } + if (!InScene) + { + return; + } + FBVERTEX pt = + { + float(x), float(y), 0, 1, color + }; + EndBatch(); // Draw out any batched operations. + SetPixelShader(Shaders[SHADER_VertexColor]); + SetAlphaBlend(GL_FUNC_ADD, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + DrawPoints(1, &pt); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: DrawTextureV +// +// If not in 2D mode, just call the normal software version. +// If in 2D mode, then use Direct3D calls to perform the drawing. +// +//========================================================================== + +void OpenGLSWFrameBuffer::DrawTextureParms(FTexture *img, DrawParms &parms) +{ + if (In2D < 2) + { + Super::DrawTextureParms(img, parms); + return; + } + if (!InScene) + { + return; + } + + OpenGLTex *tex = static_cast(img->GetNative(false)); + + if (tex == nullptr) + { + assert(tex != nullptr); + return; + } + + CheckQuadBatch(); + + double xscale = parms.destwidth / parms.texwidth; + double yscale = parms.destheight / parms.texheight; + double x0 = parms.x - parms.left * xscale; + double y0 = parms.y - parms.top * yscale; + double x1 = x0 + parms.destwidth; + double y1 = y0 + parms.destheight; + float u0 = tex->Box->Left; + float v0 = tex->Box->Top; + float u1 = tex->Box->Right; + float v1 = tex->Box->Bottom; + double uscale = 1.f / tex->Box->Owner->Width; + bool scissoring = false; + FBVERTEX *vert; + float yoffs; + + if (parms.flipX) + { + swapvalues(u0, u1); + } + if (parms.windowleft > 0 || parms.windowright < parms.texwidth) + { + double wi = MIN(parms.windowright, parms.texwidth); + x0 += parms.windowleft * xscale; + u0 = float(u0 + parms.windowleft * uscale); + x1 -= (parms.texwidth - wi) * xscale; + u1 = float(u1 - (parms.texwidth - wi) * uscale); + } + +#if 0 + float vscale = 1.f / tex->Box->Owner->Height / yscale; + if (y0 < parms.uclip) + { + v0 += (float(parms.uclip) - y0) * vscale; + y0 = float(parms.uclip); + } + if (y1 > parms.dclip) + { + v1 -= (y1 - float(parms.dclip)) * vscale; + y1 = float(parms.dclip); + } + if (x0 < parms.lclip) + { + u0 += float(parms.lclip - x0) * uscale / xscale * 2; + x0 = float(parms.lclip); + } + if (x1 > parms.rclip) + { + u1 -= (x1 - parms.rclip) * uscale / xscale * 2; + x1 = float(parms.rclip); + } +#else + // Use a scissor test because the math above introduces some jitter + // that is noticeable at low resolutions. Unfortunately, this means this + // quad has to be in a batch by itself. + if (y0 < parms.uclip || y1 > parms.dclip || x0 < parms.lclip || x1 > parms.rclip) + { + scissoring = true; + if (QuadBatchPos > 0) + { + EndQuadBatch(); + BeginQuadBatch(); + } + glEnable(GL_SCISSOR_TEST); + glScissor(parms.lclip, parms.uclip + LBOffsetI, parms.rclip - parms.lclip, parms.dclip - parms.uclip); + } +#endif + parms.bilinear = false; + + uint32_t color0, color1; + BufferedTris *quad = &QuadExtra[QuadBatchPos]; + + if (!SetStyle(tex, parms, color0, color1, *quad)) + { + goto done; + } + + quad->Texture = tex->Box->Owner->Tex; + if (parms.bilinear) + { + quad->Flags |= BQF_Bilinear; + } + quad->NumTris = 2; + quad->NumVerts = 4; + + yoffs = GatheringWipeScreen ? 0.5f : 0.5f - LBOffset; + +#if 0 + // Coordinates are truncated to integers, because that's effectively + // what the software renderer does. The hardware will instead round + // to nearest, it seems. + x0 = floorf(x0) - 0.5f; + y0 = floorf(y0) - yoffs; + x1 = floorf(x1) - 0.5f; + y1 = floorf(y1) - yoffs; +#else + x0 = x0 - 0.5f; + y0 = y0 - yoffs; + x1 = x1 - 0.5f; + y1 = y1 - yoffs; +#endif + + vert = &VertexData[VertexPos]; + + // Fill the vertex buffer. + vert[0].x = float(x0); + vert[0].y = float(y0); + vert[0].z = 0; + vert[0].rhw = 1; + vert[0].color0 = color0; + vert[0].color1 = color1; + vert[0].tu = u0; + vert[0].tv = v0; + + vert[1].x = float(x1); + vert[1].y = float(y0); + vert[1].z = 0; + vert[1].rhw = 1; + vert[1].color0 = color0; + vert[1].color1 = color1; + vert[1].tu = u1; + vert[1].tv = v0; + + vert[2].x = float(x1); + vert[2].y = float(y1); + vert[2].z = 0; + vert[2].rhw = 1; + vert[2].color0 = color0; + vert[2].color1 = color1; + vert[2].tu = u1; + vert[2].tv = v1; + + vert[3].x = float(x0); + vert[3].y = float(y1); + vert[3].z = 0; + vert[3].rhw = 1; + vert[3].color0 = color0; + vert[3].color1 = color1; + vert[3].tu = u0; + vert[3].tv = v1; + + // Fill the vertex index buffer. + IndexData[IndexPos] = VertexPos; + IndexData[IndexPos + 1] = VertexPos + 1; + IndexData[IndexPos + 2] = VertexPos + 2; + IndexData[IndexPos + 3] = VertexPos; + IndexData[IndexPos + 4] = VertexPos + 2; + IndexData[IndexPos + 5] = VertexPos + 3; + + // Batch the quad. + QuadBatchPos++; + VertexPos += 4; + IndexPos += 6; +done: + if (scissoring) + { + EndQuadBatch(); + glDisable(GL_SCISSOR_TEST); + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: FlatFill +// +// Fills an area with a repeating copy of the texture. +// +//========================================================================== + +void OpenGLSWFrameBuffer::FlatFill(int left, int top, int right, int bottom, FTexture *src, bool local_origin) +{ + if (In2D < 2) + { + Super::FlatFill(left, top, right, bottom, src, local_origin); + return; + } + if (!InScene) + { + return; + } + OpenGLTex *tex = static_cast(src->GetNative(true)); + if (tex == nullptr) + { + return; + } + float yoffs = GatheringWipeScreen ? 0.5f : 0.5f - LBOffset; + float x0 = float(left); + float y0 = float(top); + float x1 = float(right); + float y1 = float(bottom); + float itw = 1.f / float(src->GetWidth()); + float ith = 1.f / float(src->GetHeight()); + float xo = local_origin ? x0 : 0; + float yo = local_origin ? y0 : 0; + float u0 = (x0 - xo) * itw; + float v0 = (y0 - yo) * ith; + float u1 = (x1 - xo) * itw; + float v1 = (y1 - yo) * ith; + x0 -= 0.5f; + y0 -= yoffs; + x1 -= 0.5f; + y1 -= yoffs; + + CheckQuadBatch(); + + BufferedTris *quad = &QuadExtra[QuadBatchPos]; + FBVERTEX *vert = &VertexData[VertexPos]; + + quad->Group1 = 0; + if (tex->GetTexFormat() == GL_R8 && !tex->IsGray) + { + quad->Flags = BQF_WrapUV | BQF_GamePalette; // | BQF_DisableAlphaTest; + quad->ShaderNum = BQS_PalTex; + } + else + { + quad->Flags = BQF_WrapUV; // | BQF_DisableAlphaTest; + quad->ShaderNum = BQS_Plain; + } + quad->Palette = nullptr; + quad->Texture = tex->Box->Owner->Tex; + quad->NumVerts = 4; + quad->NumTris = 2; + + vert[0].x = x0; + vert[0].y = y0; + vert[0].z = 0; + vert[0].rhw = 1; + vert[0].color0 = 0; + vert[0].color1 = 0xFFFFFFFF; + vert[0].tu = u0; + vert[0].tv = v0; + + vert[1].x = x1; + vert[1].y = y0; + vert[1].z = 0; + vert[1].rhw = 1; + vert[1].color0 = 0; + vert[1].color1 = 0xFFFFFFFF; + vert[1].tu = u1; + vert[1].tv = v0; + + vert[2].x = x1; + vert[2].y = y1; + vert[2].z = 0; + vert[2].rhw = 1; + vert[2].color0 = 0; + vert[2].color1 = 0xFFFFFFFF; + vert[2].tu = u1; + vert[2].tv = v1; + + vert[3].x = x0; + vert[3].y = y1; + vert[3].z = 0; + vert[3].rhw = 1; + vert[3].color0 = 0; + vert[3].color1 = 0xFFFFFFFF; + vert[3].tu = u0; + vert[3].tv = v1; + + IndexData[IndexPos] = VertexPos; + IndexData[IndexPos + 1] = VertexPos + 1; + IndexData[IndexPos + 2] = VertexPos + 2; + IndexData[IndexPos + 3] = VertexPos; + IndexData[IndexPos + 4] = VertexPos + 2; + IndexData[IndexPos + 5] = VertexPos + 3; + + QuadBatchPos++; + VertexPos += 4; + IndexPos += 6; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: FillSimplePoly +// +// Here, "simple" means that a simple triangle fan can draw it. +// +//========================================================================== + +void OpenGLSWFrameBuffer::FillSimplePoly(FTexture *texture, FVector2 *points, int npoints, + double originx, double originy, double scalex, double scaley, + DAngle rotation, FDynamicColormap *colormap, int lightlevel) +{ + // Use an equation similar to player sprites to determine shade + double fadelevel = clamp((LIGHT2SHADE(lightlevel) / 65536. - 12) / NUMCOLORMAPS, 0.0, 1.0); + + BufferedTris *quad; + FBVERTEX *verts; + OpenGLTex *tex; + float yoffs, uscale, vscale; + int i, ipos; + uint32_t color0, color1; + float ox, oy; + float cosrot, sinrot; + bool dorotate = rotation != 0; + + if (npoints < 3) + { // This is no polygon. + return; + } + if (In2D < 2) + { + Super::FillSimplePoly(texture, points, npoints, originx, originy, scalex, scaley, rotation, colormap, lightlevel); + return; + } + if (!InScene) + { + return; + } + tex = static_cast(texture->GetNative(true)); + if (tex == nullptr) + { + return; + } + + cosrot = (float)cos(rotation.Radians()); + sinrot = (float)sin(rotation.Radians()); + + CheckQuadBatch(npoints - 2, npoints); + quad = &QuadExtra[QuadBatchPos]; + verts = &VertexData[VertexPos]; + + color0 = 0; + color1 = 0xFFFFFFFF; + + quad->Group1 = 0; + if (tex->GetTexFormat() == GL_R8 && !tex->IsGray) + { + quad->Flags = BQF_WrapUV | BQF_GamePalette | BQF_DisableAlphaTest; + quad->ShaderNum = BQS_PalTex; + if (colormap != nullptr) + { + if (colormap->Desaturate != 0) + { + quad->Flags |= BQF_Desaturated; + } + quad->ShaderNum = BQS_InGameColormap; + quad->Desat = colormap->Desaturate; + color0 = ColorARGB(255, colormap->Color.r, colormap->Color.g, colormap->Color.b); + color1 = ColorARGB(uint32_t((1 - fadelevel) * 255), + uint32_t(colormap->Fade.r * fadelevel), + uint32_t(colormap->Fade.g * fadelevel), + uint32_t(colormap->Fade.b * fadelevel)); + } + } + else + { + quad->Flags = BQF_WrapUV | BQF_DisableAlphaTest; + quad->ShaderNum = BQS_Plain; + } + quad->Palette = nullptr; + quad->Texture = tex->Box->Owner->Tex; + quad->NumVerts = npoints; + quad->NumTris = npoints - 2; + + yoffs = GatheringWipeScreen ? 0 : LBOffset; + uscale = float(1.f / (texture->GetScaledWidth() * scalex)); + vscale = float(1.f / (texture->GetScaledHeight() * scaley)); + ox = float(originx); + oy = float(originy); + + for (i = 0; i < npoints; ++i) + { + verts[i].x = points[i].X; + verts[i].y = points[i].Y + yoffs; + verts[i].z = 0; + verts[i].rhw = 1; + verts[i].color0 = color0; + verts[i].color1 = color1; + float u = points[i].X - 0.5f - ox; + float v = points[i].Y - 0.5f - oy; + if (dorotate) + { + float t = u; + u = t * cosrot - v * sinrot; + v = v * cosrot + t * sinrot; + } + verts[i].tu = u * uscale; + verts[i].tv = v * vscale; + } + for (ipos = IndexPos, i = 2; i < npoints; ++i, ipos += 3) + { + IndexData[ipos] = VertexPos; + IndexData[ipos + 1] = VertexPos + i - 1; + IndexData[ipos + 2] = VertexPos + i; + } + + QuadBatchPos++; + VertexPos += npoints; + IndexPos = ipos; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: AddColorOnlyQuad +// +// Adds a single-color, untextured quad to the batch. +// +//========================================================================== + +void OpenGLSWFrameBuffer::AddColorOnlyQuad(int left, int top, int width, int height, uint32_t color) +{ + BufferedTris *quad; + FBVERTEX *verts; + + CheckQuadBatch(); + quad = &QuadExtra[QuadBatchPos]; + verts = &VertexData[VertexPos]; + + float x = float(left) - 0.5f; + float y = float(top) - 0.5f + (GatheringWipeScreen ? 0 : LBOffset); + + quad->Group1 = 0; + quad->ShaderNum = BQS_ColorOnly; + if ((color & 0xFF000000) != 0xFF000000) + { + quad->BlendOp = GL_FUNC_ADD; + quad->SrcBlend = GL_SRC_ALPHA; + quad->DestBlend = GL_ONE_MINUS_SRC_ALPHA; + } + quad->Palette = nullptr; + quad->Texture = nullptr; + quad->NumVerts = 4; + quad->NumTris = 2; + + verts[0].x = x; + verts[0].y = y; + verts[0].z = 0; + verts[0].rhw = 1; + verts[0].color0 = color; + verts[0].color1 = 0; + verts[0].tu = 0; + verts[0].tv = 0; + + verts[1].x = x + width; + verts[1].y = y; + verts[1].z = 0; + verts[1].rhw = 1; + verts[1].color0 = color; + verts[1].color1 = 0; + verts[1].tu = 0; + verts[1].tv = 0; + + verts[2].x = x + width; + verts[2].y = y + height; + verts[2].z = 0; + verts[2].rhw = 1; + verts[2].color0 = color; + verts[2].color1 = 0; + verts[2].tu = 0; + verts[2].tv = 0; + + verts[3].x = x; + verts[3].y = y + height; + verts[3].z = 0; + verts[3].rhw = 1; + verts[3].color0 = color; + verts[3].color1 = 0; + verts[3].tu = 0; + verts[3].tv = 0; + + IndexData[IndexPos] = VertexPos; + IndexData[IndexPos + 1] = VertexPos + 1; + IndexData[IndexPos + 2] = VertexPos + 2; + IndexData[IndexPos + 3] = VertexPos; + IndexData[IndexPos + 4] = VertexPos + 2; + IndexData[IndexPos + 5] = VertexPos + 3; + + QuadBatchPos++; + VertexPos += 4; + IndexPos += 6; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: AddColorOnlyRect +// +// Like AddColorOnlyQuad, except it's hollow. +// +//========================================================================== + +void OpenGLSWFrameBuffer::AddColorOnlyRect(int left, int top, int width, int height, uint32_t color) +{ + AddColorOnlyQuad(left, top, width - 1, 1, color); // top + AddColorOnlyQuad(left + width - 1, top, 1, height - 1, color); // right + AddColorOnlyQuad(left + 1, top + height - 1, width - 1, 1, color); // bottom + AddColorOnlyQuad(left, top + 1, 1, height - 1, color); // left +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: CheckQuadBatch +// +// Make sure there's enough room in the batch for one more set of triangles. +// +//========================================================================== + +void OpenGLSWFrameBuffer::CheckQuadBatch(int numtris, int numverts) +{ + if (BatchType == BATCH_Lines) + { + EndLineBatch(); + } + else if (QuadBatchPos == MAX_QUAD_BATCH || + VertexPos + numverts > NUM_VERTS || + IndexPos + numtris * 3 > NUM_INDEXES) + { + EndQuadBatch(); + } + if (QuadBatchPos < 0) + { + BeginQuadBatch(); + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: BeginQuadBatch +// +// Locks the vertex buffer for quads and sets the cursor to 0. +// +//========================================================================== + +void OpenGLSWFrameBuffer::BeginQuadBatch() +{ + if (In2D < 2 || !InScene || QuadBatchPos >= 0) + { + return; + } + EndLineBatch(); // Make sure all lines have been drawn first. + VertexData = VertexBuffer->Lock(); + IndexData = IndexBuffer->Lock(); + VertexPos = 0; + IndexPos = 0; + QuadBatchPos = 0; + BatchType = BATCH_Quads; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: EndQuadBatch +// +// Draws all the quads that have been batched up. +// +//========================================================================== + +void OpenGLSWFrameBuffer::EndQuadBatch() +{ + if (In2D < 2 || !InScene || BatchType != BATCH_Quads) + { + return; + } + BatchType = BATCH_None; + VertexBuffer->Unlock(); + IndexBuffer->Unlock(); + if (QuadBatchPos == 0) + { + QuadBatchPos = -1; + VertexPos = -1; + IndexPos = -1; + return; + } + SetStreamSource(VertexBuffer); + SetIndices(IndexBuffer); + bool uv_wrapped = false; + bool uv_should_wrap; + int indexpos, vertpos; + + indexpos = vertpos = 0; + for (int i = 0; i < QuadBatchPos; ) + { + const BufferedTris *quad = &QuadExtra[i]; + int j; + + int startindex = indexpos; + int startvertex = vertpos; + + indexpos += quad->NumTris * 3; + vertpos += quad->NumVerts; + + // Quads with matching parameters should be done with a single + // DrawPrimitive call. + for (j = i + 1; j < QuadBatchPos; ++j) + { + const BufferedTris *q2 = &QuadExtra[j]; + if (quad->Texture != q2->Texture || + quad->Group1 != q2->Group1 || + quad->Palette != q2->Palette) + { + break; + } + if (quad->ShaderNum == BQS_InGameColormap && (quad->Flags & BQF_Desaturated) && quad->Desat != q2->Desat) + { + break; + } + indexpos += q2->NumTris * 3; + vertpos += q2->NumVerts; + } + + // Set the palette (if one) + if ((quad->Flags & BQF_Paletted) == BQF_GamePalette) + { + SetPaletteTexture(PaletteTexture, 256, BorderColor); + } + else if ((quad->Flags & BQF_Paletted) == BQF_CustomPalette) + { + assert(quad->Palette != nullptr); + SetPaletteTexture(quad->Palette->Tex, quad->Palette->RoundedPaletteSize, quad->Palette->BorderColor); + } + + // Set the alpha blending + SetAlphaBlend(quad->BlendOp, quad->SrcBlend, quad->DestBlend); + + // Set the alpha test + EnableAlphaTest(!(quad->Flags & BQF_DisableAlphaTest)); + + // Set the pixel shader + if (quad->ShaderNum == BQS_PalTex) + { + SetPixelShader(Shaders[(quad->Flags & BQF_InvertSource) ? + SHADER_NormalColorPalInv : SHADER_NormalColorPal]); + } + else if (quad->ShaderNum == BQS_Plain) + { + SetPixelShader(Shaders[(quad->Flags & BQF_InvertSource) ? + SHADER_NormalColorInv : SHADER_NormalColor]); + } + else if (quad->ShaderNum == BQS_RedToAlpha) + { + SetPixelShader(Shaders[(quad->Flags & BQF_InvertSource) ? + SHADER_RedToAlphaInv : SHADER_RedToAlpha]); + } + else if (quad->ShaderNum == BQS_ColorOnly) + { + SetPixelShader(Shaders[SHADER_VertexColor]); + } + else if (quad->ShaderNum == BQS_SpecialColormap) + { + int select; + + select = !!(quad->Flags & BQF_Paletted); + SetPixelShader(Shaders[SHADER_SpecialColormap + select]); + } + else if (quad->ShaderNum == BQS_InGameColormap) + { + int select; + + select = !!(quad->Flags & BQF_Desaturated); + select |= !!(quad->Flags & BQF_InvertSource) << 1; + select |= !!(quad->Flags & BQF_Paletted) << 2; + if (quad->Flags & BQF_Desaturated) + { + SetConstant(PSCONST_Desaturation, quad->Desat / 255.f, (255 - quad->Desat) / 255.f, 0, 0); + } + SetPixelShader(Shaders[SHADER_InGameColormap + select]); + } + + // Set the texture clamp addressing mode + uv_should_wrap = !!(quad->Flags & BQF_WrapUV); + if (uv_wrapped != uv_should_wrap) + { + uint32_t mode = uv_should_wrap ? GL_REPEAT : GL_CLAMP_TO_EDGE; + uv_wrapped = uv_should_wrap; + SetSamplerWrapS(0, mode); + SetSamplerWrapT(0, mode); + } + + // Set the texture + if (quad->Texture != nullptr) + { + SetTexture(0, quad->Texture); + } + + // Draw the quad + DrawTriangleList( + startvertex, // MinIndex + vertpos - startvertex, // NumVertices + startindex, // StartIndex + (indexpos - startindex) / 3 // PrimitiveCount + /*4 * i, 4 * (j - i), 6 * i, 2 * (j - i)*/); + i = j; + } + if (uv_wrapped) + { + SetSamplerWrapS(0, GL_CLAMP_TO_EDGE); + SetSamplerWrapT(0, GL_CLAMP_TO_EDGE); + } + QuadBatchPos = -1; + VertexPos = -1; + IndexPos = -1; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: EndBatch +// +// Draws whichever type of primitive is currently being batched. +// +//========================================================================== + +void OpenGLSWFrameBuffer::EndBatch() +{ + if (BatchType == BATCH_Quads) + { + EndQuadBatch(); + } + else if (BatchType == BATCH_Lines) + { + EndLineBatch(); + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: SetStyle +// +// Patterned after R_SetPatchStyle. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::SetStyle(OpenGLTex *tex, DrawParms &parms, uint32_t &color0, uint32_t &color1, BufferedTris &quad) +{ + int fmt = tex->GetTexFormat(); + FRenderStyle style = parms.style; + float alpha; + bool stencilling; + + if (style.Flags & STYLEF_TransSoulsAlpha) + { + alpha = transsouls; + } + else if (style.Flags & STYLEF_Alpha1) + { + alpha = 1; + } + else + { + alpha = clamp(parms.Alpha, 0.f, 1.f); + } + + style.CheckFuzz(); + if (style.BlendOp == STYLEOP_Shadow) + { + style = LegacyRenderStyles[STYLE_TranslucentStencil]; + alpha = 0.3f; + parms.fillcolor = 0; + } + + // FIXME: Fuzz effect is not written + if (style.BlendOp == STYLEOP_FuzzOrAdd || style.BlendOp == STYLEOP_Fuzz) + { + style.BlendOp = STYLEOP_Add; + } + else if (style.BlendOp == STYLEOP_FuzzOrSub) + { + style.BlendOp = STYLEOP_Sub; + } + else if (style.BlendOp == STYLEOP_FuzzOrRevSub) + { + style.BlendOp = STYLEOP_RevSub; + } + + stencilling = false; + quad.Palette = nullptr; + quad.Flags = 0; + quad.Desat = 0; + + switch (style.BlendOp) + { + default: + case STYLEOP_Add: quad.BlendOp = GL_FUNC_ADD; break; + case STYLEOP_Sub: quad.BlendOp = GL_FUNC_SUBTRACT; break; + case STYLEOP_RevSub: quad.BlendOp = GL_FUNC_REVERSE_SUBTRACT; break; + case STYLEOP_None: return false; + } + quad.SrcBlend = GetStyleAlpha(style.SrcAlpha); + quad.DestBlend = GetStyleAlpha(style.DestAlpha); + + if (style.Flags & STYLEF_InvertOverlay) + { + // Only the overlay color is inverted, not the overlay alpha. + parms.colorOverlay = ColorARGB(APART(parms.colorOverlay), + 255 - RPART(parms.colorOverlay), 255 - GPART(parms.colorOverlay), + 255 - BPART(parms.colorOverlay)); + } + + SetColorOverlay(parms.colorOverlay, alpha, color0, color1); + + if (style.Flags & STYLEF_ColorIsFixed) + { + if (style.Flags & STYLEF_InvertSource) + { // Since the source color is a constant, we can invert it now + // without spending time doing it in the shader. + parms.fillcolor = ColorXRGB(255 - RPART(parms.fillcolor), + 255 - GPART(parms.fillcolor), 255 - BPART(parms.fillcolor)); + } + // Set up the color mod to replace the color from the image data. + color0 = (color0 & ColorRGBA(0, 0, 0, 255)) | (parms.fillcolor & ColorRGBA(255, 255, 255, 0)); + color1 &= ColorRGBA(0, 0, 0, 255); + + if (style.Flags & STYLEF_RedIsAlpha) + { + // Note that if the source texture is paletted, the palette is ignored. + quad.Flags = 0; + quad.ShaderNum = BQS_RedToAlpha; + } + else if (fmt == GL_R8) + { + quad.Flags = BQF_GamePalette; + quad.ShaderNum = BQS_PalTex; + } + else + { + quad.Flags = 0; + quad.ShaderNum = BQS_Plain; + } + } + else + { + if (style.Flags & STYLEF_RedIsAlpha) + { + quad.Flags = 0; + quad.ShaderNum = BQS_RedToAlpha; + } + else if (fmt == GL_R8) + { + if (parms.remap != nullptr) + { + quad.Flags = BQF_CustomPalette; + quad.Palette = reinterpret_cast(parms.remap->GetNative()); + quad.ShaderNum = BQS_PalTex; + } + else if (tex->IsGray) + { + quad.Flags = 0; + quad.ShaderNum = BQS_Plain; + } + else + { + quad.Flags = BQF_GamePalette; + quad.ShaderNum = BQS_PalTex; + } + } + else + { + quad.Flags = 0; + quad.ShaderNum = BQS_Plain; + } + if (style.Flags & STYLEF_InvertSource) + { + quad.Flags |= BQF_InvertSource; + } + + if (parms.specialcolormap != nullptr) + { // Emulate an invulnerability or similar colormap. + float *start, *end; + start = parms.specialcolormap->ColorizeStart; + end = parms.specialcolormap->ColorizeEnd; + if (quad.Flags & BQF_InvertSource) + { + quad.Flags &= ~BQF_InvertSource; + swapvalues(start, end); + } + quad.ShaderNum = BQS_SpecialColormap; + color0 = ColorRGBA(uint32_t(start[0] / 2 * 255), uint32_t(start[1] / 2 * 255), uint32_t(start[2] / 2 * 255), color0 >> 24); + color1 = ColorRGBA(uint32_t(end[0] / 2 * 255), uint32_t(end[1] / 2 * 255), uint32_t(end[2] / 2 * 255), color1 >> 24); + } + else if (parms.colormapstyle != nullptr) + { // Emulate the fading from an in-game colormap (colorized, faded, and desaturated) + if (parms.colormapstyle->Desaturate != 0) + { + quad.Flags |= BQF_Desaturated; + } + quad.ShaderNum = BQS_InGameColormap; + quad.Desat = parms.colormapstyle->Desaturate; + color0 = ColorARGB(color1 >> 24, + parms.colormapstyle->Color.r, + parms.colormapstyle->Color.g, + parms.colormapstyle->Color.b); + double fadelevel = parms.colormapstyle->FadeLevel; + color1 = ColorARGB(uint32_t((1 - fadelevel) * 255), + uint32_t(parms.colormapstyle->Fade.r * fadelevel), + uint32_t(parms.colormapstyle->Fade.g * fadelevel), + uint32_t(parms.colormapstyle->Fade.b * fadelevel)); + } + } + + // For unmasked images, force the alpha from the image data to be ignored. + if (!parms.masked && quad.ShaderNum != BQS_InGameColormap) + { + color0 = (color0 & ColorRGBA(255, 255, 255, 0)) | ColorValue(0, 0, 0, alpha); + color1 &= ColorRGBA(255, 255, 255, 0); + + // If our alpha is one and we are doing normal adding, then we can turn the blend off completely. + if (quad.BlendOp == GL_FUNC_ADD && + ((alpha == 1 && quad.SrcBlend == GL_SRC_ALPHA) || quad.SrcBlend == GL_ONE) && + ((alpha == 1 && quad.DestBlend == GL_ONE_MINUS_SRC_ALPHA) || quad.DestBlend == GL_ZERO)) + { + quad.BlendOp = 0; + } + quad.Flags |= BQF_DisableAlphaTest; + } + return true; +} + +int OpenGLSWFrameBuffer::GetStyleAlpha(int type) +{ + switch (type) + { + case STYLEALPHA_Zero: return GL_ZERO; + case STYLEALPHA_One: return GL_ONE; + case STYLEALPHA_Src: return GL_SRC_ALPHA; + case STYLEALPHA_InvSrc: return GL_ONE_MINUS_SRC_ALPHA; + default: return GL_ZERO; + } +} + + +void OpenGLSWFrameBuffer::SetColorOverlay(uint32_t color, float alpha, uint32_t &color0, uint32_t &color1) +{ + if (APART(color) != 0) + { + int a = APART(color) * 256 / 255; + color0 = ColorRGBA( + (RPART(color) * a) >> 8, + (GPART(color) * a) >> 8, + (BPART(color) * a) >> 8, + 0); + a = 256 - a; + color1 = ColorRGBA(a, a, a, int(alpha * 255)); + } + else + { + color0 = 0; + color1 = ColorValue(1, 1, 1, alpha); + } +} + +void OpenGLSWFrameBuffer::EnableAlphaTest(BOOL enabled) +{ + if (enabled != AlphaTestEnabled) + { + AlphaTestEnabled = enabled; + //glEnable(GL_ALPHA_TEST); // To do: move to shader as this is only in the compatibility profile + } +} + +void OpenGLSWFrameBuffer::SetAlphaBlend(int op, int srcblend, int destblend) +{ + if (op == 0) + { // Disable alpha blend + if (AlphaBlendEnabled) + { + AlphaBlendEnabled = FALSE; + glDisable(GL_BLEND); + } + } + else + { // Enable alpha blend + assert(srcblend != 0); + assert(destblend != 0); + + if (!AlphaBlendEnabled) + { + AlphaBlendEnabled = TRUE; + glEnable(GL_BLEND); + } + if (AlphaBlendOp != op) + { + AlphaBlendOp = op; + glBlendEquation(op); + } + if (AlphaSrcBlend != srcblend || AlphaDestBlend != destblend) + { + AlphaSrcBlend = srcblend; + AlphaDestBlend = destblend; + glBlendFunc(srcblend, destblend); + } + } +} + +void OpenGLSWFrameBuffer::SetConstant(int cnum, float r, float g, float b, float a) +{ + if (Constant[cnum][0] != r || + Constant[cnum][1] != g || + Constant[cnum][2] != b || + Constant[cnum][3] != a) + { + Constant[cnum][0] = r; + Constant[cnum][1] = g; + Constant[cnum][2] = b; + Constant[cnum][3] = a; + SetPixelShaderConstantF(cnum, Constant[cnum], 1); + } +} + +void OpenGLSWFrameBuffer::SetPixelShader(HWPixelShader *shader) +{ + if (CurPixelShader != shader) + { + CurPixelShader = shader; + SetHWPixelShader(shader); + } +} + +void OpenGLSWFrameBuffer::SetTexture(int tnum, HWTexture *texture) +{ + assert(unsigned(tnum) < countof(Texture)); + if (texture) + { + if (Texture[tnum] != texture || SamplerWrapS[tnum] != texture->WrapS || SamplerWrapT[tnum] != texture->WrapT) + { + Texture[tnum] = texture; + glActiveTexture(GL_TEXTURE0 + tnum); + glBindTexture(GL_TEXTURE_2D, texture->Handle); + if (Texture[tnum]->WrapS != SamplerWrapS[tnum]) + { + Texture[tnum]->WrapS = SamplerWrapS[tnum]; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, SamplerWrapS[tnum]); + } + if (Texture[tnum]->WrapT != SamplerWrapT[tnum]) + { + Texture[tnum]->WrapT = SamplerWrapT[tnum]; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, SamplerWrapT[tnum]); + } + } + } + else if (Texture[tnum] != texture) + { + Texture[tnum] = texture; + glActiveTexture(GL_TEXTURE0 + tnum); + glBindTexture(GL_TEXTURE_2D, 0); + } +} + +void OpenGLSWFrameBuffer::SetSamplerWrapS(int tnum, int mode) +{ + assert(unsigned(tnum) < countof(Texture)); + if (Texture[tnum] && SamplerWrapS[tnum] != mode) + { + SamplerWrapS[tnum] = mode; + Texture[tnum]->WrapS = mode; + glActiveTexture(GL_TEXTURE0 + tnum); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, SamplerWrapS[tnum]); + } +} + +void OpenGLSWFrameBuffer::SetSamplerWrapT(int tnum, int mode) +{ + assert(unsigned(tnum) < countof(Texture)); + if (Texture[tnum] && SamplerWrapT[tnum] != mode) + { + SamplerWrapT[tnum] = mode; + Texture[tnum]->WrapT = mode; + glActiveTexture(GL_TEXTURE0 + tnum); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, SamplerWrapT[tnum]); + } +} + +void OpenGLSWFrameBuffer::SetPaletteTexture(HWTexture *texture, int count, uint32_t border_color) +{ + // The pixel shader receives color indexes in the range [0.0,1.0]. + // The palette texture is also addressed in the range [0.0,1.0], + // HOWEVER the coordinate 1.0 is the right edge of the texture and + // not actually the texture itself. We need to scale and shift + // the palette indexes so they lie exactly in the center of each + // texel. For a normal palette with 256 entries, that means the + // range we use should be [0.5,255.5], adjusted so the coordinate + // is still within [0.0,1.0]. + // + // The constant register c2 is used to hold the multiplier in the + // x part and the adder in the y part. + float fcount = 1 / float(count); + SetConstant(PSCONST_PaletteMod, 255 * fcount, 0.5f * fcount, 0, 0); + SetTexture(1, texture); +} diff --git a/src/gl/system/gl_swframebuffer.h b/src/gl/system/gl_swframebuffer.h new file mode 100644 index 000000000..30e7715ab --- /dev/null +++ b/src/gl/system/gl_swframebuffer.h @@ -0,0 +1,470 @@ +#ifndef __GL_SWFRAMEBUFFER +#define __GL_SWFRAMEBUFFER + +#ifdef _WIN32 +#include "win32iface.h" +#include "win32gliface.h" +#endif + +#include "SkylineBinPack.h" + +#include + +class FGLDebug; + +#ifdef _WIN32 +class OpenGLSWFrameBuffer : public Win32GLFrameBuffer +{ + typedef Win32GLFrameBuffer Super; + DECLARE_CLASS(OpenGLSWFrameBuffer, Win32GLFrameBuffer) +#else +#include "sdlglvideo.h" +class OpenGLFrameBuffer : public SDLGLFB +{ +// typedef SDLGLFB Super; //[C]commented, DECLARE_CLASS defines this in linux + DECLARE_CLASS(OpenGLSWFrameBuffer, SDLGLFB) +#endif + + +public: + + explicit OpenGLSWFrameBuffer() {} + OpenGLSWFrameBuffer(void *hMonitor, int width, int height, int bits, int refreshHz, bool fullscreen); + ~OpenGLSWFrameBuffer(); + + bool IsValid() override; + bool Lock(bool buffered) override; + void Unlock() override; + void Update() override; + PalEntry *GetPalette() override; + void GetFlashedPalette(PalEntry palette[256]) override; + void UpdatePalette() override; + bool SetGamma(float gamma) override; + bool SetFlash(PalEntry rgb, int amount) override; + void GetFlash(PalEntry &rgb, int &amount) override; + int GetPageCount() override; + bool IsFullscreen() override; + void PaletteChanged() override; + int QueryNewPalette() override; + void Blank() override; + bool PaintToWindow() override; + void SetVSync(bool vsync) override; + void NewRefreshRate() override; + void GetScreenshotBuffer(const uint8_t *&buffer, int &pitch, ESSType &color_type) override; + void ReleaseScreenshotBuffer() override; + void SetBlendingRect(int x1, int y1, int x2, int y2) override; + bool Begin2D(bool copy3d) override; + void DrawBlendingRect() override; + FNativeTexture *CreateTexture(FTexture *gametex, bool wrapping) override; + FNativePalette *CreatePalette(FRemapTable *remap) override; + void DrawTextureParms(FTexture *img, DrawParms &parms) override; + void Clear(int left, int top, int right, int bottom, int palcolor, uint32 color) override; + void Dim(PalEntry color, float amount, int x1, int y1, int w, int h) override; + void FlatFill(int left, int top, int right, int bottom, FTexture *src, bool local_origin) override; + void DrawLine(int x0, int y0, int x1, int y1, int palColor, uint32 realcolor) override; + void DrawPixel(int x, int y, int palcolor, uint32 rgbcolor) override; + void FillSimplePoly(FTexture *tex, FVector2 *points, int npoints, double originx, double originy, double scalex, double scaley, DAngle rotation, FDynamicColormap *colormap, int lightlevel) override; + //bool WipeStartScreen(int type) override; + //void WipeEndScreen() override; + //bool WipeDo(int ticks) override; + //void WipeCleanup() override; + bool Is8BitMode() override { return false; } + int GetTrueHeight() override { return TrueHeight; } + +private: + struct FBVERTEX + { + FLOAT x, y, z, rhw; + uint32_t color0, color1; + FLOAT tu, tv; + }; + //#define D3DFVF_FBVERTEX (D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_SPECULAR | D3DFVF_TEX1) + + struct GammaRamp + { + uint16_t red[256], green[256], blue[256]; + }; + + struct LockedRect + { + int Pitch; + void *pBits; + }; + + struct LTRBRect + { + int left, top, right, bottom; + }; + + class HWSurface + { + public: + bool LockRect(LockedRect *outRect, LTRBRect *srcRect, bool discard) { outRect->Pitch = 0; outRect->pBits = nullptr; return false; } + void UnlockRect() { } + }; + + class HWTexture + { + public: + bool LockRect(LockedRect *outRect, LTRBRect *srcRect, bool discard) { outRect->Pitch = 0; outRect->pBits = nullptr; return false; } + void UnlockRect() { } + bool GetSurfaceLevel(int level, HWSurface **outSurface) { *outSurface = nullptr; return false; } + + int Handle = 0; + int WrapS = 0; + int WrapT = 0; + int Format = 0; + }; + + class HWVertexBuffer + { + public: + FBVERTEX *Lock() { return nullptr; } + void Unlock() { } + }; + + class HWIndexBuffer + { + public: + uint16_t *Lock() { return nullptr; } + void Unlock() { } + }; + + class HWPixelShader + { + public: + }; + + bool CreatePixelShader(const void *src, HWPixelShader **outShader) { *outShader = nullptr; return false; } + bool CreateVertexBuffer(int size, HWVertexBuffer **outVertexBuffer) { *outVertexBuffer = nullptr; return false; } + bool CreateIndexBuffer(int size, HWIndexBuffer **outIndexBuffer) { *outIndexBuffer = nullptr; return false; } + bool CreateOffscreenPlainSurface(int width, int height, int format, HWSurface **outSurface) { *outSurface = nullptr; return false; } + bool CreateTexture(int width, int height, int levels, int format, HWTexture **outTexture) { *outTexture = nullptr; return false; } + bool CreateRenderTarget(int width, int height, int format, HWSurface **outSurface) { *outSurface = nullptr; return false; } + bool GetBackBuffer(HWSurface **outSurface) { *outSurface = nullptr; return false; } + bool GetRenderTarget(int index, HWSurface **outSurface) { *outSurface = nullptr; return false; } + void GetRenderTargetData(HWSurface *a, HWSurface *b) { } + void ColorFill(HWSurface *surface, float red, float green, float blue) { } + void StretchRect(HWSurface *src, const LTRBRect *srcrect, HWSurface *dest) { } + bool SetRenderTarget(int index, HWSurface *surface) { return true; } + void SetGammaRamp(const GammaRamp *ramp) { } + void SetPixelShaderConstantF(int uniformIndex, const float *data, int vec4fcount) { } + void SetHWPixelShader(HWPixelShader *shader) { } + void SetStreamSource(HWVertexBuffer *vertexBuffer) { } + void SetIndices(HWIndexBuffer *indexBuffer) { } + void DrawTriangleFans(int count, const FBVERTEX *vertices) { } + void DrawPoints(int count, const FBVERTEX *vertices) { } + void DrawLineList(int count) { } + void DrawTriangleList(int minIndex, int numVertices, int startIndex, int primitiveCount) { } + void Present() { } + + static uint32_t ColorARGB(uint32_t a, uint32_t r, uint32_t g, uint32_t b) { return ((a & 0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | ((b) & 0xff); } + static uint32_t ColorRGBA(uint32_t a, uint32_t r, uint32_t g, uint32_t b) { return ColorARGB(a, r, g, b); } + static uint32_t ColorXRGB(uint32_t r, uint32_t g, uint32_t b) { return ColorARGB(0xff, r, g, b); } + static uint32_t ColorValue(float r, float g, float b, float a) { return ColorRGBA((uint32_t)(r * 255.0f), (uint32_t)(g * 255.0f), (uint32_t)(b * 255.0f), (uint32_t)(a * 255.0f)); } + + // The number of points for the vertex buffer. + enum { NUM_VERTS = 10240 }; + + // The number of indices for the index buffer. + enum { NUM_INDEXES = ((NUM_VERTS * 6) / 4) }; + + // The number of quads we can batch together. + enum { MAX_QUAD_BATCH = (NUM_INDEXES / 6) }; + + // The default size for a texture atlas. + enum { DEF_ATLAS_WIDTH = 512 }; + enum { DEF_ATLAS_HEIGHT = 512 }; + + // TYPES ------------------------------------------------------------------- + + struct Atlas; + + struct PackedTexture + { + Atlas *Owner; + + PackedTexture **Prev, *Next; + + // Pixels this image covers + LTRBRect Area; + + // Texture coordinates for this image + float Left, Top, Right, Bottom; + + // Texture has extra space on the border? + bool Padded; + }; + + struct Atlas + { + Atlas(OpenGLSWFrameBuffer *fb, int width, int height, int format); + ~Atlas(); + + PackedTexture *AllocateImage(const Rect &rect, bool padded); + void FreeBox(PackedTexture *box); + + SkylineBinPack Packer; + Atlas *Next; + HWTexture *Tex; + int Format; + PackedTexture *UsedList; // Boxes that contain images + int Width, Height; + bool OneUse; + }; + + class OpenGLTex : public FNativeTexture + { + public: + OpenGLTex(FTexture *tex, OpenGLSWFrameBuffer *fb, bool wrapping); + ~OpenGLTex(); + + FTexture *GameTex; + PackedTexture *Box; + + OpenGLTex **Prev; + OpenGLTex *Next; + + bool IsGray; + + bool Create(OpenGLSWFrameBuffer *fb, bool wrapping); + bool Update(); + bool CheckWrapping(bool wrapping); + int GetTexFormat(); + FTextureFormat ToTexFmt(int fmt); + }; + + class OpenGLPal : public FNativePalette + { + public: + OpenGLPal(FRemapTable *remap, OpenGLSWFrameBuffer *fb); + ~OpenGLPal(); + + OpenGLPal **Prev; + OpenGLPal *Next; + + HWTexture *Tex; + uint32_t BorderColor; + bool DoColorSkip; + + bool Update(); + + FRemapTable *Remap; + int RoundedPaletteSize; + }; + + // Flags for a buffered quad + enum + { + BQF_GamePalette = 1, + BQF_CustomPalette = 7, + BQF_Paletted = 7, + BQF_Bilinear = 8, + BQF_WrapUV = 16, + BQF_InvertSource = 32, + BQF_DisableAlphaTest = 64, + BQF_Desaturated = 128, + }; + + // Shaders for a buffered quad + enum + { + BQS_PalTex, + BQS_Plain, + BQS_RedToAlpha, + BQS_ColorOnly, + BQS_SpecialColormap, + BQS_InGameColormap, + }; + + struct PackedTexture; + struct Atlas; + + struct BufferedTris + { + union + { + struct + { + uint8_t Flags; + uint8_t ShaderNum : 4; + int BlendOp; + int SrcBlend, DestBlend; + }; + uint32_t Group1; + }; + uint8_t Desat; + OpenGLPal *Palette; + HWTexture *Texture; + uint16_t NumVerts; // Number of _unique_ vertices used by this set. + uint16_t NumTris; // Number of triangles used by this set. + }; + + enum + { + PSCONST_Desaturation = 1, + PSCONST_PaletteMod = 2, + PSCONST_Weights = 6, + PSCONST_Gamma = 7, + }; + enum + { + SHADER_NormalColor, + SHADER_NormalColorPal, + SHADER_NormalColorInv, + SHADER_NormalColorPalInv, + + SHADER_RedToAlpha, + SHADER_RedToAlphaInv, + + SHADER_VertexColor, + + SHADER_SpecialColormap, + SHADER_SpecialColormapPal, + + SHADER_InGameColormap, + SHADER_InGameColormapDesat, + SHADER_InGameColormapInv, + SHADER_InGameColormapInvDesat, + SHADER_InGameColormapPal, + SHADER_InGameColormapPalDesat, + SHADER_InGameColormapPalInv, + SHADER_InGameColormapPalInvDesat, + + SHADER_BurnWipe, + SHADER_GammaCorrection, + + NUM_SHADERS + }; + static const char *const ShaderNames[NUM_SHADERS]; + + void Flip(); + void SetInitialState(); + bool CreateResources(); + void ReleaseResources(); + bool LoadShaders(); + void CreateBlockSurfaces(); + bool CreateFBTexture(); + bool CreatePaletteTexture(); + bool CreateGammaTexture(); + bool CreateVertexes(); + void UploadPalette(); + void UpdateGammaTexture(float igamma); + void CalcFullscreenCoords(FBVERTEX verts[4], bool viewarea_only, bool can_double, uint32_t color0, uint32_t color1) const; + bool Reset(); + HWTexture *GetCurrentScreen(); + void ReleaseDefaultPoolItems(); + void KillNativePals(); + void KillNativeTexs(); + PackedTexture *AllocPackedTexture(int width, int height, bool wrapping, int format); + void DrawPackedTextures(int packnum); + void DrawLetterbox(); + void Draw3DPart(bool copy3d); + bool SetStyle(OpenGLTex *tex, DrawParms &parms, uint32_t &color0, uint32_t &color1, BufferedTris &quad); + static int GetStyleAlpha(int type); + static void SetColorOverlay(uint32_t color, float alpha, uint32_t &color0, uint32_t &color1); + void DoWindowedGamma(); + void AddColorOnlyQuad(int left, int top, int width, int height, uint32_t color); + void AddColorOnlyRect(int left, int top, int width, int height, uint32_t color); + void CheckQuadBatch(int numtris = 2, int numverts = 4); + void BeginQuadBatch(); + void EndQuadBatch(); + void BeginLineBatch(); + void EndLineBatch(); + void EndBatch(); + void CopyNextFrontBuffer(); + + // State + void EnableAlphaTest(BOOL enabled); + void SetAlphaBlend(int op, int srcblend = 0, int destblend = 0); + void SetConstant(int cnum, float r, float g, float b, float a); + void SetPixelShader(HWPixelShader *shader); + void SetTexture(int tnum, HWTexture *texture); + void SetSamplerWrapS(int tnum, int mode); + void SetSamplerWrapT(int tnum, int mode); + void SetPaletteTexture(HWTexture *texture, int count, uint32_t border_color); + + template static void SafeRelease(T &x) { if (x != nullptr) { delete x; x = nullptr; } } + + BOOL AlphaTestEnabled; + BOOL AlphaBlendEnabled; + int AlphaBlendOp; + int AlphaSrcBlend; + int AlphaDestBlend; + float Constant[3][4]; + uint32_t CurBorderColor; + HWPixelShader *CurPixelShader; + HWTexture *Texture[5]; + int SamplerWrapS[5], SamplerWrapT[5]; + + PalEntry SourcePalette[256]; + uint32_t BorderColor; + uint32_t FlashColor0, FlashColor1; + PalEntry FlashColor; + int FlashAmount; + int TrueHeight; + int PixelDoubling; + int SkipAt; + int LBOffsetI; + int RenderTextureToggle; + int CurrRenderTexture; + float LBOffset; + float Gamma; + bool UpdatePending; + bool NeedPalUpdate; + bool NeedGammaUpdate; + int FBWidth, FBHeight; + bool VSync; + LTRBRect BlendingRect; + int In2D; + bool InScene; + bool GatheringWipeScreen; + bool AALines; + uint8_t BlockNum; + OpenGLPal *Palettes; + OpenGLTex *Textures; + Atlas *Atlases; + + HWTexture *FBTexture; + HWTexture *TempRenderTexture, *RenderTexture[2]; + HWTexture *PaletteTexture; + HWTexture *GammaTexture; + HWTexture *ScreenshotTexture; + HWSurface *ScreenshotSurface; + HWSurface *FrontCopySurface; + + HWVertexBuffer *VertexBuffer; + FBVERTEX *VertexData; + HWIndexBuffer *IndexBuffer; + uint16_t *IndexData; + BufferedTris *QuadExtra; + int VertexPos; + int IndexPos; + int QuadBatchPos; + enum { BATCH_None, BATCH_Quads, BATCH_Lines } BatchType; + + HWPixelShader *Shaders[NUM_SHADERS]; + HWPixelShader *GammaShader; + + HWSurface *BlockSurface[2]; + HWSurface *OldRenderTarget; + HWTexture *InitialWipeScreen, *FinalWipeScreen; + + class Wiper + { + public: + virtual ~Wiper(); + virtual bool Run(int ticks, OpenGLSWFrameBuffer *fb) = 0; + + //void DrawScreen(OpenGLSWFrameBuffer *fb, HWTexture *tex, int blendop = 0, uint32_t color0 = 0, uint32_t color1 = 0xFFFFFFF); + }; + + class Wiper_Melt; friend class Wiper_Melt; + class Wiper_Burn; friend class Wiper_Burn; + class Wiper_Crossfade; friend class Wiper_Crossfade; + + Wiper *ScreenWipe; +}; + + +#endif //__GL_SWFRAMEBUFFER diff --git a/src/textures/textures.h b/src/textures/textures.h index 477c39ecc..af72dabc0 100644 --- a/src/textures/textures.h +++ b/src/textures/textures.h @@ -292,6 +292,7 @@ public: static void FlipNonSquareBlockRemap (BYTE *blockto, const BYTE *blockfrom, int x, int y, int srcpitch, const BYTE *remap); friend class D3DTex; + friend class OpenGLSWFrameBuffer; public: From 40df46f94e250000f471870229931a00a027b32f Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Mon, 10 Oct 2016 07:39:02 +0200 Subject: [PATCH 2/7] Implement OpenGL versions of GPU objects --- src/gl/system/gl_swframebuffer.cpp | 313 +++++++++++++++++++++++++---- src/gl/system/gl_swframebuffer.h | 75 ++++--- 2 files changed, 327 insertions(+), 61 deletions(-) diff --git a/src/gl/system/gl_swframebuffer.cpp b/src/gl/system/gl_swframebuffer.cpp index e0ff2be47..e03f1fd9c 100644 --- a/src/gl/system/gl_swframebuffer.cpp +++ b/src/gl/system/gl_swframebuffer.cpp @@ -92,30 +92,30 @@ IMPLEMENT_CLASS(OpenGLSWFrameBuffer) const char *const OpenGLSWFrameBuffer::ShaderNames[OpenGLSWFrameBuffer::NUM_SHADERS] = { - "NormalColor.fp", - "NormalColorPal.fp", - "NormalColorInv.fp", - "NormalColorPalInv.fp", + "NormalColor", + "NormalColorPal", + "NormalColorInv", + "NormalColorPalInv", - "RedToAlpha.fp", - "RedToAlphaInv.fp", + "RedToAlpha", + "RedToAlphaInv", - "VertexColor.fp", + "VertexColor", - "SpecialColormap.fp", - "SpecialColorMapPal.fp", + "SpecialColormap", + "SpecialColorMapPal", - "InGameColormap.fp", - "InGameColormapDesat.fp", - "InGameColormapInv.fp", - "InGameColormapInvDesat.fp", - "InGameColormapPal.fp", - "InGameColormapPalDesat.fp", - "InGameColormapPalInv.fp", - "InGameColormapPalInvDesat.fp", + "InGameColormap", + "InGameColormapDesat", + "InGameColormapInv", + "InGameColormapInvDesat", + "InGameColormapPal", + "InGameColormapPalDesat", + "InGameColormapPalInv", + "InGameColormapPalInvDesat", - "BurnWipe.fp", - "GammaCorrection.fp", + "BurnWipe", + "GammaCorrection", }; OpenGLSWFrameBuffer::OpenGLSWFrameBuffer(void *hMonitor, int width, int height, int bits, int refreshHz, bool fullscreen) : @@ -209,6 +209,256 @@ OpenGLSWFrameBuffer::~OpenGLSWFrameBuffer() delete[] QuadExtra; } +bool OpenGLSWFrameBuffer::CreatePixelShader(const void *vertexsrc, const void *fragmentsrc, HWPixelShader **outShader) +{ + auto shader = std::make_unique(); + + shader->Program = glCreateProgram(); + shader->VertexShader = glCreateShader(GL_VERTEX_SHADER); + shader->FragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + + { + int lengths[1] = { (int)strlen((const char*)vertexsrc) }; + const char *sources[1] = { (const char*)vertexsrc }; + glShaderSource(shader->VertexShader, 1, sources, lengths); + glCompileShader(shader->VertexShader); + } + + { + int lengths[1] = { (int)strlen((const char*)fragmentsrc) }; + const char *sources[1] = { (const char*)fragmentsrc }; + glShaderSource(shader->FragmentShader, 1, sources, lengths); + glCompileShader(shader->FragmentShader); + } + + GLint status = 0; + glGetShaderiv(shader->VertexShader, GL_COMPILE_STATUS, &status); + if (status != GL_FALSE) glGetShaderiv(shader->FragmentShader, GL_COMPILE_STATUS, &status); + if (status == GL_FALSE) + { + *outShader = nullptr; + return false; + } + + glAttachShader(shader->Program, shader->VertexShader); + glAttachShader(shader->Program, shader->FragmentShader); + glBindFragDataLocation(shader->Program, 0, "FragColor"); + glLinkProgram(shader->Program); + glGetProgramiv(shader->Program, GL_LINK_STATUS, &status); + if (status == GL_FALSE) + { + *outShader = nullptr; + return false; + } + glBindAttribLocation(shader->Program, 0, "Position"); + glBindAttribLocation(shader->Program, 1, "Color0"); + glBindAttribLocation(shader->Program, 2, "Color1"); + glBindAttribLocation(shader->Program, 3, "TexCoord"); + + *outShader = shader.release(); + return true; +} + +bool OpenGLSWFrameBuffer::CreateVertexBuffer(int size, HWVertexBuffer **outVertexBuffer) +{ + auto obj = std::make_unique(); + + GLint oldBinding = 0; + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &oldBinding); + + glGenVertexArrays(1, (GLuint*)&obj->VertexArray); + glGenBuffers(1, (GLuint*)&obj->Buffer); + glBindVertexArray(obj->VertexArray); + glBindBuffer(GL_ARRAY_BUFFER, obj->Buffer); + glBufferData(GL_ARRAY_BUFFER, size, nullptr, GL_STREAM_DRAW); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, x)); + glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, color0)); + glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, color1)); + glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, tu)); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(oldBinding); + + *outVertexBuffer = obj.release(); + return true; +} + +bool OpenGLSWFrameBuffer::CreateIndexBuffer(int size, HWIndexBuffer **outIndexBuffer) +{ + auto obj = std::make_unique(); + + GLint oldBinding = 0; + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &oldBinding); + + glGenBuffers(1, (GLuint*)&obj->Buffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, obj->Buffer); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, nullptr, GL_STREAM_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, oldBinding); + + *outIndexBuffer = obj.release(); + return true; +} + +bool OpenGLSWFrameBuffer::CreateTexture(int width, int height, int levels, int format, HWTexture **outTexture) +{ + auto obj = std::make_unique(); + + GLint oldBinding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldBinding); + + glGenTextures(1, (GLuint*)&obj->Texture); + glBindTexture(GL_TEXTURE_2D, obj->Texture); + GLenum srcformat; + switch (format) + { + case GL_R8: srcformat = GL_RED; break; + case GL_RGBA8: srcformat = GL_RGBA; break; + case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: srcformat = GL_RGB; break; + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: srcformat = GL_RGBA; break; + case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: srcformat = GL_RGBA; break; + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: srcformat = GL_RGBA; break; + default: + I_FatalError("Unknown format passed to CreateTexture"); + return false; + } + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, srcformat, GL_UNSIGNED_BYTE, nullptr); + + glBindTexture(GL_TEXTURE_2D, oldBinding); + + *outTexture = obj.release(); + return true; +} + +bool OpenGLSWFrameBuffer::CreateOffscreenPlainSurface(int width, int height, int format, HWSurface **outSurface) { *outSurface = nullptr; return false; } +bool OpenGLSWFrameBuffer::CreateRenderTarget(int width, int height, int format, HWSurface **outSurface) { *outSurface = nullptr; return false; } +bool OpenGLSWFrameBuffer::GetBackBuffer(HWSurface **outSurface) { *outSurface = nullptr; return false; } +bool OpenGLSWFrameBuffer::GetRenderTarget(int index, HWSurface **outSurface) { *outSurface = nullptr; return false; } +void OpenGLSWFrameBuffer::GetRenderTargetData(HWSurface *a, HWSurface *b) { } + +void OpenGLSWFrameBuffer::ColorFill(HWSurface *surface, float red, float green, float blue) { } +void OpenGLSWFrameBuffer::StretchRect(HWSurface *src, const LTRBRect *srcrect, HWSurface *dest) { } +bool OpenGLSWFrameBuffer::SetRenderTarget(int index, HWSurface *surface) { return true; } +void OpenGLSWFrameBuffer::SetGammaRamp(const GammaRamp *ramp) { } + +void OpenGLSWFrameBuffer::SetPixelShaderConstantF(int uniformIndex, const float *data, int vec4fcount) +{ + glUniform4fv(uniformIndex, vec4fcount, data); +} + +void OpenGLSWFrameBuffer::SetHWPixelShader(HWPixelShader *shader) +{ + if (shader) + glUseProgram(shader->Program); + else + glUseProgram(0); +} + +void OpenGLSWFrameBuffer::SetStreamSource(HWVertexBuffer *vertexBuffer) +{ + if (vertexBuffer) + glBindVertexArray(vertexBuffer->VertexArray); + else + glBindVertexArray(0); +} + +void OpenGLSWFrameBuffer::SetIndices(HWIndexBuffer *indexBuffer) +{ + if (indexBuffer) + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer->Buffer); + else + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + +void OpenGLSWFrameBuffer::DrawTriangleFans(int count, const FBVERTEX *vertices) +{ + GLint oldBinding = 0; + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &oldBinding); + + if (!StreamVertexBuffer) + { + StreamVertexBuffer = std::make_unique(); + glGenVertexArrays(1, (GLuint*)&StreamVertexBuffer->VertexArray); + glGenBuffers(1, (GLuint*)&StreamVertexBuffer->Buffer); + glBindVertexArray(StreamVertexBuffer->VertexArray); + glBindBuffer(GL_ARRAY_BUFFER, StreamVertexBuffer->Buffer); + glBufferData(GL_ARRAY_BUFFER, count * sizeof(FBVERTEX), vertices, GL_STREAM_DRAW); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, x)); + glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, color0)); + glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, color1)); + glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, tu)); + } + else + { + glBindVertexArray(StreamVertexBuffer->VertexArray); + glBindBuffer(GL_ARRAY_BUFFER, StreamVertexBuffer->Buffer); + glBufferData(GL_ARRAY_BUFFER, count * sizeof(FBVERTEX), vertices, GL_STREAM_DRAW); + } + + glDrawArrays(GL_TRIANGLE_FAN, 0, count); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(oldBinding); +} + +void OpenGLSWFrameBuffer::DrawPoints(int count, const FBVERTEX *vertices) +{ + GLint oldBinding = 0; + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &oldBinding); + + if (!StreamVertexBuffer) + { + StreamVertexBuffer = std::make_unique(); + glGenVertexArrays(1, (GLuint*)&StreamVertexBuffer->VertexArray); + glGenBuffers(1, (GLuint*)&StreamVertexBuffer->Buffer); + glBindVertexArray(StreamVertexBuffer->VertexArray); + glBindBuffer(GL_ARRAY_BUFFER, StreamVertexBuffer->Buffer); + glBufferData(GL_ARRAY_BUFFER, count * sizeof(FBVERTEX), vertices, GL_STREAM_DRAW); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + glEnableVertexAttribArray(3); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, x)); + glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, color0)); + glVertexAttribPointer(2, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, color1)); + glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(FBVERTEX), (const GLvoid*)offsetof(FBVERTEX, tu)); + } + else + { + glBindVertexArray(StreamVertexBuffer->VertexArray); + glBindBuffer(GL_ARRAY_BUFFER, StreamVertexBuffer->Buffer); + glBufferData(GL_ARRAY_BUFFER, count * sizeof(FBVERTEX), vertices, GL_STREAM_DRAW); + } + + glDrawArrays(GL_POINTS, 0, count); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(oldBinding); +} + +void OpenGLSWFrameBuffer::DrawLineList(int count) +{ + glDrawArrays(GL_LINES, 0, count); +} + +void OpenGLSWFrameBuffer::DrawTriangleList(int minIndex, int numVertices, int startIndex, int primitiveCount) +{ + glDrawRangeElements(GL_TRIANGLES, minIndex, minIndex + numVertices - 1, primitiveCount * 3, GL_UNSIGNED_SHORT, (const void*)(startIndex * sizeof(uint16_t))); +} + +void OpenGLSWFrameBuffer::Present() +{ + SwapBuffers(); +} + //========================================================================== // // OpenGLSWFrameBuffer :: SetInitialState @@ -274,7 +524,6 @@ bool OpenGLSWFrameBuffer::CreateResources() { return false; } - CreateGammaTexture(); CreateBlockSurfaces(); return true; } @@ -293,7 +542,7 @@ bool OpenGLSWFrameBuffer::LoadShaders() static const char models[][4] = { "30/", "20/", "14/" }; FString shaderdir, shaderpath; unsigned model, i; - int lump; + int lump, lumpvert; // We determine the best available model simply by trying them all in // order of decreasing preference. @@ -304,12 +553,13 @@ bool OpenGLSWFrameBuffer::LoadShaders() for (i = 0; i < NUM_SHADERS; ++i) { shaderpath = shaderdir; - shaderpath += ShaderNames[i]; - lump = Wads.CheckNumForFullName(shaderpath); - if (lump >= 0) + lump = Wads.CheckNumForFullName(shaderpath + ShaderNames[i] + ".fp"); + lumpvert = Wads.CheckNumForFullName(shaderpath + ShaderNames[i] + ".vp"); + if (lump >= 0 && lumpvert >= 0) { FMemLump data = Wads.ReadLump(lump); - if (!CreatePixelShader((uint32_t *)data.GetMem(), &Shaders[i]) && i < SHADER_BurnWipe) + FMemLump datavert = Wads.ReadLump(lumpvert); + if (!CreatePixelShader(datavert.GetMem(), data.GetMem(), &Shaders[i]) && i < SHADER_BurnWipe) { break; } @@ -533,17 +783,6 @@ bool OpenGLSWFrameBuffer::CreatePaletteTexture() return true; } -//========================================================================== -// -// OpenGLSWFrameBuffer :: CreateGammaTexture -// -//========================================================================== - -bool OpenGLSWFrameBuffer::CreateGammaTexture() -{ - return false; -} - //========================================================================== // // OpenGLSWFrameBuffer :: CreateVertexes @@ -3293,7 +3532,7 @@ void OpenGLSWFrameBuffer::SetTexture(int tnum, HWTexture *texture) { Texture[tnum] = texture; glActiveTexture(GL_TEXTURE0 + tnum); - glBindTexture(GL_TEXTURE_2D, texture->Handle); + glBindTexture(GL_TEXTURE_2D, texture->Texture); if (Texture[tnum]->WrapS != SamplerWrapS[tnum]) { Texture[tnum]->WrapS = SamplerWrapS[tnum]; diff --git a/src/gl/system/gl_swframebuffer.h b/src/gl/system/gl_swframebuffer.h index 30e7715ab..14ed2ec13 100644 --- a/src/gl/system/gl_swframebuffer.h +++ b/src/gl/system/gl_swframebuffer.h @@ -110,7 +110,7 @@ private: void UnlockRect() { } bool GetSurfaceLevel(int level, HWSurface **outSurface) { *outSurface = nullptr; return false; } - int Handle = 0; + int Texture = 0; int WrapS = 0; int WrapT = 0; int Format = 0; @@ -119,44 +119,70 @@ private: class HWVertexBuffer { public: + ~HWVertexBuffer() + { + if (Buffer != 0) glDeleteVertexArrays(1, (GLuint*)&VertexArray); + if (Buffer != 0) glDeleteBuffers(1, (GLuint*)&Buffer); + } + FBVERTEX *Lock() { return nullptr; } void Unlock() { } + + int Buffer = 0; + int VertexArray = 0; }; class HWIndexBuffer { public: + ~HWIndexBuffer() + { + if (Buffer != 0) glDeleteBuffers(1, (GLuint*)&Buffer); + } + uint16_t *Lock() { return nullptr; } void Unlock() { } + + int Buffer = 0; }; class HWPixelShader { public: + ~HWPixelShader() + { + if (Program != 0) glDeleteProgram(Program); + if (VertexShader != 0) glDeleteShader(VertexShader); + if (FragmentShader != 0) glDeleteShader(FragmentShader); + } + + int Program = 0; + int VertexShader = 0; + int FragmentShader = 0; }; - bool CreatePixelShader(const void *src, HWPixelShader **outShader) { *outShader = nullptr; return false; } - bool CreateVertexBuffer(int size, HWVertexBuffer **outVertexBuffer) { *outVertexBuffer = nullptr; return false; } - bool CreateIndexBuffer(int size, HWIndexBuffer **outIndexBuffer) { *outIndexBuffer = nullptr; return false; } - bool CreateOffscreenPlainSurface(int width, int height, int format, HWSurface **outSurface) { *outSurface = nullptr; return false; } - bool CreateTexture(int width, int height, int levels, int format, HWTexture **outTexture) { *outTexture = nullptr; return false; } - bool CreateRenderTarget(int width, int height, int format, HWSurface **outSurface) { *outSurface = nullptr; return false; } - bool GetBackBuffer(HWSurface **outSurface) { *outSurface = nullptr; return false; } - bool GetRenderTarget(int index, HWSurface **outSurface) { *outSurface = nullptr; return false; } - void GetRenderTargetData(HWSurface *a, HWSurface *b) { } - void ColorFill(HWSurface *surface, float red, float green, float blue) { } - void StretchRect(HWSurface *src, const LTRBRect *srcrect, HWSurface *dest) { } - bool SetRenderTarget(int index, HWSurface *surface) { return true; } - void SetGammaRamp(const GammaRamp *ramp) { } - void SetPixelShaderConstantF(int uniformIndex, const float *data, int vec4fcount) { } - void SetHWPixelShader(HWPixelShader *shader) { } - void SetStreamSource(HWVertexBuffer *vertexBuffer) { } - void SetIndices(HWIndexBuffer *indexBuffer) { } - void DrawTriangleFans(int count, const FBVERTEX *vertices) { } - void DrawPoints(int count, const FBVERTEX *vertices) { } - void DrawLineList(int count) { } - void DrawTriangleList(int minIndex, int numVertices, int startIndex, int primitiveCount) { } - void Present() { } + bool CreatePixelShader(const void *vertexsrc, const void *fragmentsrc, HWPixelShader **outShader); + bool CreateVertexBuffer(int size, HWVertexBuffer **outVertexBuffer); + bool CreateIndexBuffer(int size, HWIndexBuffer **outIndexBuffer); + bool CreateOffscreenPlainSurface(int width, int height, int format, HWSurface **outSurface); + bool CreateTexture(int width, int height, int levels, int format, HWTexture **outTexture); + bool CreateRenderTarget(int width, int height, int format, HWSurface **outSurface); + bool GetBackBuffer(HWSurface **outSurface); + bool GetRenderTarget(int index, HWSurface **outSurface); + void GetRenderTargetData(HWSurface *a, HWSurface *b); + void ColorFill(HWSurface *surface, float red, float green, float blue); + void StretchRect(HWSurface *src, const LTRBRect *srcrect, HWSurface *dest); + bool SetRenderTarget(int index, HWSurface *surface); + void SetGammaRamp(const GammaRamp *ramp); + void SetPixelShaderConstantF(int uniformIndex, const float *data, int vec4fcount); + void SetHWPixelShader(HWPixelShader *shader); + void SetStreamSource(HWVertexBuffer *vertexBuffer); + void SetIndices(HWIndexBuffer *indexBuffer); + void DrawTriangleFans(int count, const FBVERTEX *vertices); + void DrawPoints(int count, const FBVERTEX *vertices); + void DrawLineList(int count); + void DrawTriangleList(int minIndex, int numVertices, int startIndex, int primitiveCount); + void Present(); static uint32_t ColorARGB(uint32_t a, uint32_t r, uint32_t g, uint32_t b) { return ((a & 0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | ((b) & 0xff); } static uint32_t ColorRGBA(uint32_t a, uint32_t r, uint32_t g, uint32_t b) { return ColorARGB(a, r, g, b); } @@ -346,7 +372,6 @@ private: void CreateBlockSurfaces(); bool CreateFBTexture(); bool CreatePaletteTexture(); - bool CreateGammaTexture(); bool CreateVertexes(); void UploadPalette(); void UpdateGammaTexture(float igamma); @@ -386,6 +411,8 @@ private: template static void SafeRelease(T &x) { if (x != nullptr) { delete x; x = nullptr; } } + std::unique_ptr StreamVertexBuffer; + BOOL AlphaTestEnabled; BOOL AlphaBlendEnabled; int AlphaBlendOp; From f4308b3184ea1f5ad15f3883c60be7cf2f6515bc Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Mon, 10 Oct 2016 21:03:55 +0200 Subject: [PATCH 3/7] Add glsl shader version of the d3d9 shaders and get enough of it working for it to boot without errors --- src/gl/system/gl_swframebuffer.cpp | 616 ++++++++++--------------- src/gl/system/gl_swframebuffer.h | 99 ++-- src/win32/hardware.cpp | 5 +- src/win32/win32gliface.cpp | 6 +- wadsrc/static/shaders/glsl/swshader.fp | 146 ++++++ wadsrc/static/shaders/glsl/swshader.vp | 17 + 6 files changed, 435 insertions(+), 454 deletions(-) create mode 100644 wadsrc/static/shaders/glsl/swshader.fp create mode 100644 wadsrc/static/shaders/glsl/swshader.vp diff --git a/src/gl/system/gl_swframebuffer.cpp b/src/gl/system/gl_swframebuffer.cpp index e03f1fd9c..895508d8b 100644 --- a/src/gl/system/gl_swframebuffer.cpp +++ b/src/gl/system/gl_swframebuffer.cpp @@ -71,7 +71,6 @@ #include "gl/utility/gl_templates.h" #include "gl/gl_functions.h" -CVAR(Bool, gl_antilag, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) CVAR(Int, gl_showpacks, 0, 0) #ifndef WIN32 // Defined in fb_d3d9 for Windows CVAR(Bool, vid_hwaalines, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) @@ -88,63 +87,66 @@ EXTERN_CVAR(Int, vid_refreshrate) extern cycle_t BlitCycles; +void gl_LoadExtensions(); + IMPLEMENT_CLASS(OpenGLSWFrameBuffer) -const char *const OpenGLSWFrameBuffer::ShaderNames[OpenGLSWFrameBuffer::NUM_SHADERS] = +const char *const OpenGLSWFrameBuffer::ShaderDefines[OpenGLSWFrameBuffer::NUM_SHADERS] = { - "NormalColor", - "NormalColorPal", - "NormalColorInv", - "NormalColorPalInv", + "#define ENORMALCOLOR\n#define PALTEX 0\n#define DINVERT 0", // NormalColor + "#define ENORMALCOLOR\n#define PALTEX 1\n#define INVERT 0", // NormalColorPal + "#define ENORMALCOLOR\n#define PALTEX 0\n#define INVERT 1", // NormalColorInv + "#define ENORMALCOLOR\n#define PALTEX 1\n#define INVERT 1", // NormalColorPalInv - "RedToAlpha", - "RedToAlphaInv", + "#define EREDTOALPHA\n#define INVERT 0", // RedToAlpha + "#define EREDTOALPHA\n#define INVERT 1", // RedToAlphaInv - "VertexColor", + "#define EVERTEXCOLOR", // VertexColor - "SpecialColormap", - "SpecialColorMapPal", + "#define ESPECIALCOLORMAP\n#define PALTEX 0\n#define INVERT 0", // SpecialColormap + "#define ESPECIALCOLORMAP\n#define PALTEX 1\n#define INVERT 0", // SpecialColorMapPal - "InGameColormap", - "InGameColormapDesat", - "InGameColormapInv", - "InGameColormapInvDesat", - "InGameColormapPal", - "InGameColormapPalDesat", - "InGameColormapPalInv", - "InGameColormapPalInvDesat", + "#define EINGAMECOLORMAP\n#define PALTEX 0\n#define INVERT 0\n#define DESAT 0", // InGameColormap + "#define EINGAMECOLORMAP\n#define PALTEX 0\n#define INVERT 0\n#define DESAT 1", // InGameColormapDesat + "#define EINGAMECOLORMAP\n#define PALTEX 0\n#define INVERT 1\n#define DESAT 0", // InGameColormapInv + "#define EINGAMECOLORMAP\n#define PALTEX 0\n#define INVERT 1\n#define DESAT 1", // InGameColormapInvDesat + "#define EINGAMECOLORMAP\n#define PALTEX 1\n#define INVERT 0\n#define DESAT 0", // InGameColormapPal + "#define EINGAMECOLORMAP\n#define PALTEX 1\n#define INVERT 0\n#define DESAT 1", // InGameColormapPalDesat + "#define EINGAMECOLORMAP\n#define PALTEX 1\n#define INVERT 1\n#define DESAT 0", // InGameColormapPalInv + "#define EINGAMECOLORMAP\n#define PALTEX 1\n#define INVERT 1\n#define DESAT 1", // InGameColormapPalInvDesat - "BurnWipe", - "GammaCorrection", + "#define EBURNWIPE", // BurnWipe + "#define EGAMMACORRECTION", // GammaCorrection }; OpenGLSWFrameBuffer::OpenGLSWFrameBuffer(void *hMonitor, int width, int height, int bits, int refreshHz, bool fullscreen) : Super(hMonitor, width, height, bits, refreshHz, fullscreen) { + // To do: this needs to cooperate with the same static in OpenGLFrameBuffer::InitializeState + static bool first = true; + if (first) + { + ogl_LoadFunctions(); + } + gl_LoadExtensions(); + Super::InitializeState(); + // SetVSync needs to be at the very top to workaround a bug in Nvidia's OpenGL driver. // If wglSwapIntervalEXT is called after glBindFramebuffer in a frame the setting is not changed! - SetVSync(vid_vsync); + //SetVSync(vid_vsync); VertexBuffer = nullptr; IndexBuffer = nullptr; FBTexture = nullptr; - TempRenderTexture = nullptr; - RenderTexture[0] = nullptr; - RenderTexture[1] = nullptr; InitialWipeScreen = nullptr; ScreenshotTexture = nullptr; - ScreenshotSurface = nullptr; FinalWipeScreen = nullptr; PaletteTexture = nullptr; - GammaTexture = nullptr; - FrontCopySurface = nullptr; for (int i = 0; i < NUM_SHADERS; ++i) { Shaders[i] = nullptr; } GammaShader = nullptr; - BlockSurface[0] = nullptr; - BlockSurface[1] = nullptr; VSync = vid_vsync; BlendingRect.left = 0; BlendingRect.top = 0; @@ -160,9 +162,6 @@ OpenGLSWFrameBuffer::OpenGLSWFrameBuffer(void *hMonitor, int width, int height, QuadExtra = new BufferedTris[MAX_QUAD_BATCH]; Atlases = nullptr; PixelDoubling = 0; - SkipAt = -1; - CurrRenderTexture = 0; - RenderTextureToggle = 0; Gamma = 1.0; FlashColor0 = 0; @@ -180,7 +179,7 @@ OpenGLSWFrameBuffer::OpenGLSWFrameBuffer(void *hMonitor, int width, int height, memcpy(SourcePalette, GPalette.BaseColors, sizeof(PalEntry) * 256); - Windowed = !(static_cast(Video)->GoFullscreen(fullscreen)); + //Windowed = !(static_cast(Video)->GoFullscreen(fullscreen)); TrueHeight = height; /*if (fullscreen) @@ -209,7 +208,56 @@ OpenGLSWFrameBuffer::~OpenGLSWFrameBuffer() delete[] QuadExtra; } -bool OpenGLSWFrameBuffer::CreatePixelShader(const void *vertexsrc, const void *fragmentsrc, HWPixelShader **outShader) +OpenGLSWFrameBuffer::HWTexture::~HWTexture() +{ + if (Texture != 0) glDeleteTextures(1, (GLuint*)&Texture); + if (Buffer != 0) glDeleteBuffers(1, (GLuint*)&Buffer); +} + +OpenGLSWFrameBuffer::HWVertexBuffer::~HWVertexBuffer() +{ + if (VertexArray != 0) glDeleteVertexArrays(1, (GLuint*)&VertexArray); + if (Buffer != 0) glDeleteBuffers(1, (GLuint*)&Buffer); +} + +OpenGLSWFrameBuffer::FBVERTEX *OpenGLSWFrameBuffer::HWVertexBuffer::Lock() +{ + glBindBuffer(GL_ARRAY_BUFFER, Buffer); + return (FBVERTEX*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); +} + +void OpenGLSWFrameBuffer::HWVertexBuffer::Unlock() +{ + glUnmapBuffer(GL_ARRAY_BUFFER); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +OpenGLSWFrameBuffer::HWIndexBuffer::~HWIndexBuffer() +{ + if (Buffer != 0) glDeleteBuffers(1, (GLuint*)&Buffer); +} + +uint16_t *OpenGLSWFrameBuffer::HWIndexBuffer::Lock() +{ + glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &LockedOldBinding); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Buffer); + return (uint16_t*)glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); +} + +void OpenGLSWFrameBuffer::HWIndexBuffer::Unlock() +{ + glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, LockedOldBinding); +} + +OpenGLSWFrameBuffer::HWPixelShader::~HWPixelShader() +{ + if (Program != 0) glDeleteProgram(Program); + if (VertexShader != 0) glDeleteShader(VertexShader); + if (FragmentShader != 0) glDeleteShader(FragmentShader); +} + +bool OpenGLSWFrameBuffer::CreatePixelShader(FString vertexsrc, FString fragmentsrc, const FString &defines, HWPixelShader **outShader) { auto shader = std::make_unique(); @@ -217,25 +265,34 @@ bool OpenGLSWFrameBuffer::CreatePixelShader(const void *vertexsrc, const void *f shader->VertexShader = glCreateShader(GL_VERTEX_SHADER); shader->FragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + vertexsrc = "#version 130\n" + defines + "\n#line 0\n" + vertexsrc; + fragmentsrc = "#version 130\n" + defines + "\n#line 0\n" + fragmentsrc; + { - int lengths[1] = { (int)strlen((const char*)vertexsrc) }; - const char *sources[1] = { (const char*)vertexsrc }; + int lengths[1] = { (int)vertexsrc.Len() }; + const char *sources[1] = { vertexsrc.GetChars() }; glShaderSource(shader->VertexShader, 1, sources, lengths); glCompileShader(shader->VertexShader); } { - int lengths[1] = { (int)strlen((const char*)fragmentsrc) }; - const char *sources[1] = { (const char*)fragmentsrc }; + int lengths[1] = { (int)fragmentsrc.Len() }; + const char *sources[1] = { fragmentsrc.GetChars() }; glShaderSource(shader->FragmentShader, 1, sources, lengths); glCompileShader(shader->FragmentShader); } GLint status = 0; + int errorShader = shader->VertexShader; glGetShaderiv(shader->VertexShader, GL_COMPILE_STATUS, &status); - if (status != GL_FALSE) glGetShaderiv(shader->FragmentShader, GL_COMPILE_STATUS, &status); + if (status != GL_FALSE) { errorShader = shader->FragmentShader; glGetShaderiv(shader->FragmentShader, GL_COMPILE_STATUS, &status); } if (status == GL_FALSE) { + static char buffer[10000]; + GLsizei length = 0; + buffer[0] = 0; + glGetShaderInfoLog(errorShader, 10000, &length, buffer); + *outShader = nullptr; return false; } @@ -308,6 +365,8 @@ bool OpenGLSWFrameBuffer::CreateTexture(int width, int height, int levels, int f { auto obj = std::make_unique(); + obj->Format = format; + GLint oldBinding = 0; glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldBinding); @@ -317,7 +376,7 @@ bool OpenGLSWFrameBuffer::CreateTexture(int width, int height, int levels, int f switch (format) { case GL_R8: srcformat = GL_RED; break; - case GL_RGBA8: srcformat = GL_RGBA; break; + case GL_RGBA8: srcformat = GL_BGRA; break; case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: srcformat = GL_RGB; break; case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: srcformat = GL_RGBA; break; case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: srcformat = GL_RGBA; break; @@ -327,6 +386,8 @@ bool OpenGLSWFrameBuffer::CreateTexture(int width, int height, int levels, int f return false; } glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, srcformat, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glBindTexture(GL_TEXTURE_2D, oldBinding); @@ -334,16 +395,9 @@ bool OpenGLSWFrameBuffer::CreateTexture(int width, int height, int levels, int f return true; } -bool OpenGLSWFrameBuffer::CreateOffscreenPlainSurface(int width, int height, int format, HWSurface **outSurface) { *outSurface = nullptr; return false; } -bool OpenGLSWFrameBuffer::CreateRenderTarget(int width, int height, int format, HWSurface **outSurface) { *outSurface = nullptr; return false; } -bool OpenGLSWFrameBuffer::GetBackBuffer(HWSurface **outSurface) { *outSurface = nullptr; return false; } -bool OpenGLSWFrameBuffer::GetRenderTarget(int index, HWSurface **outSurface) { *outSurface = nullptr; return false; } -void OpenGLSWFrameBuffer::GetRenderTargetData(HWSurface *a, HWSurface *b) { } - -void OpenGLSWFrameBuffer::ColorFill(HWSurface *surface, float red, float green, float blue) { } -void OpenGLSWFrameBuffer::StretchRect(HWSurface *src, const LTRBRect *srcrect, HWSurface *dest) { } -bool OpenGLSWFrameBuffer::SetRenderTarget(int index, HWSurface *surface) { return true; } -void OpenGLSWFrameBuffer::SetGammaRamp(const GammaRamp *ramp) { } +void OpenGLSWFrameBuffer::SetGammaRamp(const GammaRamp *ramp) +{ +} void OpenGLSWFrameBuffer::SetPixelShaderConstantF(int uniformIndex, const float *data, int vec4fcount) { @@ -487,7 +541,6 @@ void OpenGLSWFrameBuffer::SetInitialState() NeedGammaUpdate = true; NeedPalUpdate = true; - OldRenderTarget = nullptr; // This constant is used for grayscaling weights (.xyz) and color inversion (.w) float weights[4] = { 77 / 256.f, 143 / 256.f, 37 / 256.f, 1 }; @@ -524,7 +577,6 @@ bool OpenGLSWFrameBuffer::CreateResources() { return false; } - CreateBlockSurfaces(); return true; } @@ -539,42 +591,36 @@ bool OpenGLSWFrameBuffer::CreateResources() bool OpenGLSWFrameBuffer::LoadShaders() { - static const char models[][4] = { "30/", "20/", "14/" }; + int lumpvert = Wads.CheckNumForFullName("shaders/glsl/swshader.vp"); + int lumpfrag = Wads.CheckNumForFullName("shaders/glsl/swshader.fp"); + if (lumpvert < 0 || lumpfrag < 0) + return false; + + FString vertsource = Wads.ReadLump(lumpvert).GetString(); + FString fragsource = Wads.ReadLump(lumpfrag).GetString(); + FString shaderdir, shaderpath; - unsigned model, i; - int lump, lumpvert; + unsigned int i; // We determine the best available model simply by trying them all in // order of decreasing preference. - for (model = 0; model < countof(models); ++model) + for (i = 0; i < NUM_SHADERS; ++i) { - shaderdir = "shaders/gl/sm"; - shaderdir += models[model]; - for (i = 0; i < NUM_SHADERS; ++i) + shaderpath = shaderdir; + if (!CreatePixelShader(vertsource, fragsource, ShaderDefines[i], &Shaders[i]) && i < SHADER_BurnWipe) { - shaderpath = shaderdir; - lump = Wads.CheckNumForFullName(shaderpath + ShaderNames[i] + ".fp"); - lumpvert = Wads.CheckNumForFullName(shaderpath + ShaderNames[i] + ".vp"); - if (lump >= 0 && lumpvert >= 0) - { - FMemLump data = Wads.ReadLump(lump); - FMemLump datavert = Wads.ReadLump(lumpvert); - if (!CreatePixelShader(datavert.GetMem(), data.GetMem(), &Shaders[i]) && i < SHADER_BurnWipe) - { - break; - } - } - } - if (i == NUM_SHADERS) - { // Success! - return true; - } - // Failure. Release whatever managed to load (which is probably nothing.) - for (i = 0; i < NUM_SHADERS; ++i) - { - SafeRelease(Shaders[i]); + break; } } + if (i == NUM_SHADERS) + { // Success! + return true; + } + // Failure. Release whatever managed to load (which is probably nothing.) + for (i = 0; i < NUM_SHADERS; ++i) + { + SafeRelease(Shaders[i]); + } return false; } @@ -590,7 +636,6 @@ void OpenGLSWFrameBuffer::ReleaseResources() KillNativeTexs(); KillNativePals(); ReleaseDefaultPoolItems(); - SafeRelease(ScreenshotSurface); SafeRelease(ScreenshotTexture); SafeRelease(PaletteTexture); for (int i = 0; i < NUM_SHADERS; ++i) @@ -616,14 +661,9 @@ void OpenGLSWFrameBuffer::ReleaseDefaultPoolItems() { SafeRelease(FBTexture); SafeRelease(FinalWipeScreen); - SafeRelease(RenderTexture[0]); - SafeRelease(RenderTexture[1]); SafeRelease(InitialWipeScreen); SafeRelease(VertexBuffer); SafeRelease(IndexBuffer); - SafeRelease(BlockSurface[0]); - SafeRelease(BlockSurface[1]); - SafeRelease(FrontCopySurface); } bool OpenGLSWFrameBuffer::Reset() @@ -633,32 +673,10 @@ bool OpenGLSWFrameBuffer::Reset() { return false; } - CreateBlockSurfaces(); SetInitialState(); return true; } -//========================================================================== -// -// OpenGLSWFrameBuffer :: CreateBlockSurfaces -// -// Create blocking surfaces for antilag. It's okay if these can't be -// created; antilag just won't work. -// -//========================================================================== - -void OpenGLSWFrameBuffer::CreateBlockSurfaces() -{ - BlockNum = 0; - if (CreateOffscreenPlainSurface(16, 16, GL_RGBA8, &BlockSurface[0])) - { - if (!CreateOffscreenPlainSurface(16, 16, GL_RGBA8, &BlockSurface[1])) - { - SafeRelease(BlockSurface[0]); - } - } -} - //========================================================================== // // OpenGLSWFrameBuffer :: KillNativePals @@ -691,80 +709,11 @@ void OpenGLSWFrameBuffer::KillNativeTexs() } } -//========================================================================== -// -// OpenGLSWFrameBuffer :: CreateFBTexture -// -// Creates the "Framebuffer" texture. With the advent of hardware-assisted -// 2D, this is something of a misnomer now. The FBTexture is only used for -// uploading the software 3D image to video memory so that it can be drawn -// to the real frame buffer. -// -// It also creates the TempRenderTexture, since this seemed like a -// convenient place to do so. -// -//========================================================================== - bool OpenGLSWFrameBuffer::CreateFBTexture() { - if (!CreateTexture(Width, Height, 1, GL_R8, &FBTexture)) - { - int pow2width, pow2height, i; - - for (i = 1; i < Width; i <<= 1) {} pow2width = i; - for (i = 1; i < Height; i <<= 1) {} pow2height = i; - - if (!CreateTexture(pow2width, pow2height, 1, GL_R8, &FBTexture)) - { - return false; - } - else - { - FBWidth = pow2width; - FBHeight = pow2height; - } - } - else - { - FBWidth = Width; - FBHeight = Height; - } - RenderTextureToggle = 0; - RenderTexture[0] = nullptr; - RenderTexture[1] = nullptr; - if (!CreateTexture(FBWidth, FBHeight, 1, GL_RGBA8, &RenderTexture[0])) - { - return false; - } - if (Windowed || PixelDoubling) - { - // Windowed or pixel doubling: Create another render texture so we can flip between them. - RenderTextureToggle = 1; - if (!CreateTexture(FBWidth, FBHeight, 1, GL_RGBA8, &RenderTexture[1])) - { - return false; - } - } - else - { - // Fullscreen and not pixel doubling: Create a render target to have the back buffer copied to. - if (!CreateRenderTarget(Width, Height, GL_RGBA8, &FrontCopySurface)) - { - return false; - } - } - // Initialize the TempRenderTextures to black. - for (int i = 0; i <= RenderTextureToggle; ++i) - { - HWSurface *surf; - if (RenderTexture[i]->GetSurfaceLevel(0, &surf)) - { - ColorFill(surf, 0.0f, 0.0f, 0.0f); - delete surf; - } - } - TempRenderTexture = RenderTexture[0]; - CurrRenderTexture = 0; + CreateTexture(Width, Height, 1, GL_R8, &FBTexture); + FBWidth = Width; + FBHeight = Height; return true; } @@ -814,7 +763,7 @@ bool OpenGLSWFrameBuffer::CreateVertexes() void OpenGLSWFrameBuffer::CalcFullscreenCoords(FBVERTEX verts[4], bool viewarea_only, bool can_double, uint32_t color0, uint32_t color1) const { - float offset = OldRenderTarget != nullptr ? 0 : LBOffset; + float offset = LBOffset;//OldRenderTarget != nullptr ? 0 : LBOffset; float top = offset - 0.5f; float texright = float(Width) / float(FBWidth); float texbot = float(Height) / float(FBHeight); @@ -1042,18 +991,6 @@ void OpenGLSWFrameBuffer::Update() LOG("SetGammaRamp\n"); SetGammaRamp(&ramp); } - else - { - if (igamma != 1) - { - UpdateGammaTexture(igamma); - GammaShader = Shaders[SHADER_GammaCorrection]; - } - else - { - GammaShader = nullptr; - } - } psgamma[2] = psgamma[1] = psgamma[0] = igamma; psgamma[3] = 0.5; // For SM14 version SetPixelShaderConstantF(PSCONST_Gamma, psgamma, 1); @@ -1094,21 +1031,6 @@ void OpenGLSWFrameBuffer::Flip() DrawLetterbox(); DoWindowedGamma(); - CopyNextFrontBuffer(); - - // Attempt to counter input lag. - if (gl_antilag && BlockSurface[0] != nullptr) - { - LockedRect lr; - volatile int dummy; - ColorFill(BlockSurface[BlockNum], 0.0f, 0x20/255.0f, 0x50/255.0f); - BlockNum ^= 1; - if (BlockSurface[BlockNum]->LockRect(&lr, nullptr, false)) - { - dummy = *(int *)lr.pBits; - BlockSurface[BlockNum]->UnlockRect(); - } - } // Limiting the frame rate is as simple as waiting for the timer to signal this event. if (FPSLimitEvent != nullptr) { @@ -1117,13 +1039,6 @@ void OpenGLSWFrameBuffer::Flip() Present(); InScene = false; - if (RenderTextureToggle) - { - // Flip the TempRenderTexture to the other one now. - CurrRenderTexture ^= RenderTextureToggle; - TempRenderTexture = RenderTexture[CurrRenderTexture]; - } - if (Windowed) { int clientWidth = GetClientWidth(); @@ -1143,42 +1058,6 @@ void OpenGLSWFrameBuffer::Flip() } } -//========================================================================== -// -// OpenGLSWFrameBuffer :: CopyNextFrontBuffer -// -// Duplicates the contents of the back buffer that will become the front -// buffer upon Present into FrontCopySurface so that we can get the -// contents of the display without wasting time in GetFrontBufferData(). -// -//========================================================================== - -void OpenGLSWFrameBuffer::CopyNextFrontBuffer() -{ - HWSurface *backbuff; - - if (Windowed || PixelDoubling) - { - // Windowed mode or pixel doubling: TempRenderTexture has what we want - SafeRelease(FrontCopySurface); - if (TempRenderTexture->GetSurfaceLevel(0, &backbuff)) - { - FrontCopySurface = backbuff; - } - } - else - { - // Fullscreen, not pixel doubled: The back buffer has what we want, - // but it might be letter boxed. - if (GetBackBuffer(&backbuff)) - { - LTRBRect srcrect = { 0, LBOffsetI, Width, LBOffsetI + Height }; - StretchRect(backbuff, &srcrect, FrontCopySurface); - delete backbuff; - } - } -} - //========================================================================== // // OpenGLSWFrameBuffer :: PaintToWindow @@ -1207,50 +1086,49 @@ void OpenGLSWFrameBuffer::Draw3DPart(bool copy3d) { if (copy3d) { - LTRBRect texrect = { 0, 0, Width, Height }; - LockedRect lockrect; - - if ((FBWidth == Width && FBHeight == Height && - FBTexture->LockRect(&lockrect, nullptr, true)) || - FBTexture->LockRect(&lockrect, &texrect, false)) + if (FBTexture->Buffer == 0) { - if (lockrect.Pitch == Pitch && Pitch == Width) + glGenBuffers(1, (GLuint*)&FBTexture->Buffer); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, FBTexture->Buffer); + glBufferData(GL_PIXEL_UNPACK_BUFFER, Width * Height, nullptr, GL_STREAM_DRAW); + } + else + { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, FBTexture->Buffer); + } + + uint8_t *dest = (uint8_t*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); + if (dest) + { + if (Pitch == Width) { - memcpy(lockrect.pBits, MemBuffer, Width * Height); + memcpy(dest, MemBuffer, Width * Height); } else { - uint8_t *dest = (uint8_t *)lockrect.pBits; uint8_t *src = MemBuffer; for (int y = 0; y < Height; y++) { memcpy(dest, src, Width); - dest += lockrect.Pitch; + dest += Width; src += Pitch; } } - FBTexture->UnlockRect(); + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + GLint oldBinding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldBinding); + glBindTexture(GL_TEXTURE_2D, FBTexture->Texture); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, Width, Height, GL_R8, GL_UNSIGNED_BYTE, 0); + glBindTexture(GL_TEXTURE_2D, oldBinding); } + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); } InScene = true; if (vid_hwaalines) glEnable(GL_LINE_SMOOTH); else glDisable(GL_LINE_SMOOTH); - assert(OldRenderTarget == nullptr); - if (TempRenderTexture != nullptr && - ((Windowed && TempRenderTexture != FinalWipeScreen) || GatheringWipeScreen || PixelDoubling)) - { - HWSurface *targetsurf; - if (TempRenderTexture->GetSurfaceLevel(0, &targetsurf)) - { - if (GetRenderTarget(0, &OldRenderTarget)) - { - SetRenderTarget(0, targetsurf); - } - delete targetsurf; - } - } SetTexture(0, FBTexture); SetPaletteTexture(PaletteTexture, 256, BorderColor); @@ -1271,10 +1149,8 @@ void OpenGLSWFrameBuffer::Draw3DPart(bool copy3d) } else { - color0 = ColorValue(realfixedcolormap->ColorizeStart[0] / 2, - realfixedcolormap->ColorizeStart[1] / 2, realfixedcolormap->ColorizeStart[2] / 2, 0); - color1 = ColorValue(realfixedcolormap->ColorizeEnd[0] / 2, - realfixedcolormap->ColorizeEnd[1] / 2, realfixedcolormap->ColorizeEnd[2] / 2, 1); + color0 = ColorValue(realfixedcolormap->ColorizeStart[0] / 2, realfixedcolormap->ColorizeStart[1] / 2, realfixedcolormap->ColorizeStart[2] / 2, 0); + color1 = ColorValue(realfixedcolormap->ColorizeEnd[0] / 2, realfixedcolormap->ColorizeEnd[1] / 2, realfixedcolormap->ColorizeEnd[2] / 2, 1); SetPixelShader(Shaders[SHADER_SpecialColormapPal]); } } @@ -1323,7 +1199,7 @@ void OpenGLSWFrameBuffer::DrawLetterbox() void OpenGLSWFrameBuffer::DoWindowedGamma() { - if (OldRenderTarget != nullptr) + /*if (OldRenderTarget != nullptr) { FBVERTEX verts[4]; @@ -1336,49 +1212,28 @@ void OpenGLSWFrameBuffer::DoWindowedGamma() DrawTriangleFans(2, verts); delete OldRenderTarget; OldRenderTarget = nullptr; - } -} - -//========================================================================== -// -// OpenGLSWFrameBuffer :: UpdateGammaTexture -// -// Updates the gamma texture used by the PS14 shader. We only use the first -// half of the texture so that we needn't worry about imprecision causing -// it to grab from the border. -// -//========================================================================== - -void OpenGLSWFrameBuffer::UpdateGammaTexture(float igamma) -{ - LockedRect lockrect; - - if (GammaTexture != nullptr && GammaTexture->LockRect(&lockrect, nullptr, false)) - { - uint8_t *pix = (uint8_t *)lockrect.pBits; - for (int i = 0; i <= 128; ++i) - { - pix[i * 4 + 2] = pix[i * 4 + 1] = pix[i * 4] = uint8_t(255.f * powf(i / 128.f, igamma)); - pix[i * 4 + 3] = 255; - } - GammaTexture->UnlockRect(); - } + }*/ } void OpenGLSWFrameBuffer::UploadPalette() { - LockedRect lockrect; - - if (SkipAt < 0) + if (PaletteTexture->Buffer == 0) { - SkipAt = 256; + glGenBuffers(1, (GLuint*)&PaletteTexture->Buffer); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, PaletteTexture->Buffer); + glBufferData(GL_PIXEL_UNPACK_BUFFER, 256 * 4, nullptr, GL_STREAM_DRAW); } - if (PaletteTexture->LockRect(&lockrect, nullptr, false)) + else + { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, PaletteTexture->Buffer); + } + + uint8_t *pix = (uint8_t *)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); + if (pix) { - uint8_t *pix = (uint8_t *)lockrect.pBits; int i; - for (i = 0; i < SkipAt; ++i, pix += 4) + for (i = 0; i < 256; ++i, pix += 4) { pix[0] = SourcePalette[i].b; pix[1] = SourcePalette[i].g; @@ -1394,9 +1249,16 @@ void OpenGLSWFrameBuffer::UploadPalette() pix[2] = SourcePalette[i].r; pix[3] = 255; } - PaletteTexture->UnlockRect(); + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + GLint oldBinding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldBinding); + glBindTexture(GL_TEXTURE_2D, PaletteTexture->Texture); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 1, GL_BGRA, GL_UNSIGNED_BYTE, 0); + glBindTexture(GL_TEXTURE_2D, oldBinding); BorderColor = ColorXRGB(SourcePalette[255].r, SourcePalette[255].g, SourcePalette[255].b); } + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); } PalEntry *OpenGLSWFrameBuffer::GetPalette() @@ -1484,6 +1346,8 @@ void OpenGLSWFrameBuffer::SetBlendingRect(int x1, int y1, int x2, int y2) void OpenGLSWFrameBuffer::GetScreenshotBuffer(const uint8_t *&buffer, int &pitch, ESSType &color_type) { + Super::GetScreenshotBuffer(buffer, pitch, color_type); + /* LockedRect lrect; if (!Accel2D) @@ -1513,6 +1377,7 @@ void OpenGLSWFrameBuffer::GetScreenshotBuffer(const uint8_t *&buffer, int &pitch color_type = SS_BGRA; } } + */ } //========================================================================== @@ -1527,58 +1392,9 @@ void OpenGLSWFrameBuffer::ReleaseScreenshotBuffer() { Super::ReleaseScreenshotBuffer(); } - if (ScreenshotSurface != nullptr) - { - ScreenshotSurface->UnlockRect(); - delete ScreenshotSurface; - ScreenshotSurface = nullptr; - } SafeRelease(ScreenshotTexture); } -//========================================================================== -// -// OpenGLSWFrameBuffer :: GetCurrentScreen -// -// Returns a texture containing the pixels currently visible on-screen. -// -//========================================================================== - -OpenGLSWFrameBuffer::HWTexture *OpenGLSWFrameBuffer::GetCurrentScreen() -{ - HWTexture *tex; - HWSurface *surf; - bool hr; - - if (FrontCopySurface == nullptr) - { - return nullptr; - } - - hr = CreateTexture(FBWidth, FBHeight, 1, GL_RGBA8, &tex); - - if (!hr) - { - return nullptr; - } - if (!tex->GetSurfaceLevel(0, &surf)) - { - delete tex; - return nullptr; - } - - // Video -> System memory : use GetRenderTargetData - GetRenderTargetData(FrontCopySurface, surf); - delete surf; - - if (!hr) - { - delete tex; - return nullptr; - } - return tex; -} - /**************************************************************************/ /* 2D Stuff */ /**************************************************************************/ @@ -2013,7 +1829,6 @@ bool OpenGLSWFrameBuffer::OpenGLTex::Create(OpenGLSWFrameBuffer *fb, bool wrappi bool OpenGLSWFrameBuffer::OpenGLTex::Update() { - LockedRect lrect; LTRBRect rect; uint8_t *dest; @@ -2025,27 +1840,44 @@ bool OpenGLSWFrameBuffer::OpenGLTex::Update() int format = Box->Owner->Tex->Format; rect = Box->Area; - if (!Box->Owner->Tex->LockRect(&lrect, &rect, false)) + + if (Box->Owner->Tex->Buffer == 0) + glGenBuffers(1, (GLuint*)&Box->Owner->Tex->Buffer); + + int bytesPerPixel = 4; + switch (format) + { + case GL_R8: bytesPerPixel = 1; break; + case GL_RGBA8: bytesPerPixel = 4; break; + default: return false; + } + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, Box->Owner->Tex->Buffer); + glBufferData(GL_PIXEL_UNPACK_BUFFER, (rect.right - rect.left) * (rect.bottom - rect.top) * bytesPerPixel, nullptr, GL_STREAM_DRAW); + + int pitch = (rect.right - rect.left) * bytesPerPixel; + uint8_t *bits = (uint8_t *)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); + dest = bits; + if (!dest) { return false; } - dest = (uint8_t *)lrect.pBits; if (Box->Padded) { - dest += lrect.Pitch + (format == GL_R8 ? 1 : 4); + dest += pitch + (format == GL_R8 ? 1 : 4); } - GameTex->FillBuffer(dest, lrect.Pitch, GameTex->GetHeight(), ToTexFmt(format)); + GameTex->FillBuffer(dest, pitch, GameTex->GetHeight(), ToTexFmt(format)); if (Box->Padded) { // Clear top padding row. - dest = (uint8_t *)lrect.pBits; + dest = bits; int numbytes = GameTex->GetWidth() + 2; if (format != GL_R8) { numbytes <<= 2; } memset(dest, 0, numbytes); - dest += lrect.Pitch; + dest += pitch; // Clear left and right padding columns. if (format == GL_R8) { @@ -2053,7 +1885,7 @@ bool OpenGLSWFrameBuffer::OpenGLTex::Update() { dest[0] = 0; dest[numbytes - 1] = 0; - dest += lrect.Pitch; + dest += pitch; } } else @@ -2062,13 +1894,20 @@ bool OpenGLSWFrameBuffer::OpenGLTex::Update() { *(uint32_t *)dest = 0; *(uint32_t *)(dest + numbytes - 4) = 0; - dest += lrect.Pitch; + dest += pitch; } } // Clear bottom padding row. memset(dest, 0, numbytes); } - Box->Owner->Tex->UnlockRect(); + + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + GLint oldBinding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldBinding); + glBindTexture(GL_TEXTURE_2D, Box->Owner->Tex->Texture); + glTexSubImage2D(GL_TEXTURE_2D, 0, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, format == GL_RGBA8 ? GL_BGRA : GL_RED, GL_UNSIGNED_BYTE, 0); + glBindTexture(GL_TEXTURE_2D, oldBinding); + glBindTexture(GL_PIXEL_UNPACK_BUFFER, 0); return true; } @@ -2199,18 +2038,28 @@ OpenGLSWFrameBuffer::OpenGLPal::~OpenGLPal() bool OpenGLSWFrameBuffer::OpenGLPal::Update() { - LockedRect lrect; uint32_t *buff; const PalEntry *pal; int skipat, i; assert(Tex != nullptr); - if (!Tex->LockRect(&lrect, nullptr, 0)) + if (Tex->Buffer == 0) + { + glGenBuffers(1, (GLuint*)&Tex->Buffer); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, Tex->Buffer); + glBufferData(GL_PIXEL_UNPACK_BUFFER, Remap->NumEntries * 4, nullptr, GL_STREAM_DRAW); + } + else + { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, Tex->Buffer); + } + + buff = (uint32_t *)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); + if (buff == nullptr) { return false; } - buff = (uint32_t *)lrect.pBits; pal = Remap->Palette; // See explanation in UploadPalette() for skipat rationale. @@ -2226,7 +2075,14 @@ bool OpenGLSWFrameBuffer::OpenGLPal::Update() } BorderColor = ColorARGB(pal[i].a, pal[i - 1].r, pal[i - 1].g, pal[i - 1].b); - Tex->UnlockRect(); + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + GLint oldBinding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldBinding); + glBindTexture(GL_TEXTURE_2D, Tex->Texture); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, Remap->NumEntries, 1, GL_BGRA, GL_UNSIGNED_BYTE, 0); + glBindTexture(GL_TEXTURE_2D, oldBinding); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + return true; } diff --git a/src/gl/system/gl_swframebuffer.h b/src/gl/system/gl_swframebuffer.h index 14ed2ec13..eed9b6f7f 100644 --- a/src/gl/system/gl_swframebuffer.h +++ b/src/gl/system/gl_swframebuffer.h @@ -85,32 +85,18 @@ private: uint16_t red[256], green[256], blue[256]; }; - struct LockedRect - { - int Pitch; - void *pBits; - }; - struct LTRBRect { int left, top, right, bottom; }; - class HWSurface - { - public: - bool LockRect(LockedRect *outRect, LTRBRect *srcRect, bool discard) { outRect->Pitch = 0; outRect->pBits = nullptr; return false; } - void UnlockRect() { } - }; - class HWTexture { public: - bool LockRect(LockedRect *outRect, LTRBRect *srcRect, bool discard) { outRect->Pitch = 0; outRect->pBits = nullptr; return false; } - void UnlockRect() { } - bool GetSurfaceLevel(int level, HWSurface **outSurface) { *outSurface = nullptr; return false; } + ~HWTexture(); int Texture = 0; + int Buffer = 0; int WrapS = 0; int WrapT = 0; int Format = 0; @@ -119,60 +105,43 @@ private: class HWVertexBuffer { public: - ~HWVertexBuffer() - { - if (Buffer != 0) glDeleteVertexArrays(1, (GLuint*)&VertexArray); - if (Buffer != 0) glDeleteBuffers(1, (GLuint*)&Buffer); - } + ~HWVertexBuffer(); - FBVERTEX *Lock() { return nullptr; } - void Unlock() { } + FBVERTEX *Lock(); + void Unlock(); - int Buffer = 0; int VertexArray = 0; + int Buffer = 0; }; class HWIndexBuffer { public: - ~HWIndexBuffer() - { - if (Buffer != 0) glDeleteBuffers(1, (GLuint*)&Buffer); - } + ~HWIndexBuffer(); - uint16_t *Lock() { return nullptr; } - void Unlock() { } + uint16_t *Lock(); + void Unlock(); int Buffer = 0; + + private: + int LockedOldBinding = 0; }; class HWPixelShader { public: - ~HWPixelShader() - { - if (Program != 0) glDeleteProgram(Program); - if (VertexShader != 0) glDeleteShader(VertexShader); - if (FragmentShader != 0) glDeleteShader(FragmentShader); - } + ~HWPixelShader(); int Program = 0; int VertexShader = 0; int FragmentShader = 0; }; - bool CreatePixelShader(const void *vertexsrc, const void *fragmentsrc, HWPixelShader **outShader); + bool CreatePixelShader(FString vertexsrc, FString fragmentsrc, const FString &defines, HWPixelShader **outShader); bool CreateVertexBuffer(int size, HWVertexBuffer **outVertexBuffer); bool CreateIndexBuffer(int size, HWIndexBuffer **outIndexBuffer); - bool CreateOffscreenPlainSurface(int width, int height, int format, HWSurface **outSurface); bool CreateTexture(int width, int height, int levels, int format, HWTexture **outTexture); - bool CreateRenderTarget(int width, int height, int format, HWSurface **outSurface); - bool GetBackBuffer(HWSurface **outSurface); - bool GetRenderTarget(int index, HWSurface **outSurface); - void GetRenderTargetData(HWSurface *a, HWSurface *b); - void ColorFill(HWSurface *surface, float red, float green, float blue); - void StretchRect(HWSurface *src, const LTRBRect *srcrect, HWSurface *dest); - bool SetRenderTarget(int index, HWSurface *surface); void SetGammaRamp(const GammaRamp *ramp); void SetPixelShaderConstantF(int uniformIndex, const float *data, int vec4fcount); void SetHWPixelShader(HWPixelShader *shader); @@ -362,19 +331,17 @@ private: NUM_SHADERS }; - static const char *const ShaderNames[NUM_SHADERS]; + static const char *const ShaderDefines[NUM_SHADERS]; void Flip(); void SetInitialState(); bool CreateResources(); void ReleaseResources(); bool LoadShaders(); - void CreateBlockSurfaces(); bool CreateFBTexture(); bool CreatePaletteTexture(); bool CreateVertexes(); void UploadPalette(); - void UpdateGammaTexture(float igamma); void CalcFullscreenCoords(FBVERTEX verts[4], bool viewarea_only, bool can_double, uint32_t color0, uint32_t color1) const; bool Reset(); HWTexture *GetCurrentScreen(); @@ -397,7 +364,6 @@ private: void BeginLineBatch(); void EndLineBatch(); void EndBatch(); - void CopyNextFrontBuffer(); // State void EnableAlphaTest(BOOL enabled); @@ -431,10 +397,7 @@ private: int FlashAmount; int TrueHeight; int PixelDoubling; - int SkipAt; int LBOffsetI; - int RenderTextureToggle; - int CurrRenderTexture; float LBOffset; float Gamma; bool UpdatePending; @@ -448,34 +411,28 @@ private: bool GatheringWipeScreen; bool AALines; uint8_t BlockNum; - OpenGLPal *Palettes; - OpenGLTex *Textures; - Atlas *Atlases; + OpenGLPal *Palettes = nullptr; + OpenGLTex *Textures = nullptr; + Atlas *Atlases = nullptr; - HWTexture *FBTexture; - HWTexture *TempRenderTexture, *RenderTexture[2]; - HWTexture *PaletteTexture; - HWTexture *GammaTexture; - HWTexture *ScreenshotTexture; - HWSurface *ScreenshotSurface; - HWSurface *FrontCopySurface; + HWTexture *FBTexture = nullptr; + HWTexture *PaletteTexture = nullptr; + HWTexture *ScreenshotTexture = nullptr; - HWVertexBuffer *VertexBuffer; - FBVERTEX *VertexData; - HWIndexBuffer *IndexBuffer; - uint16_t *IndexData; - BufferedTris *QuadExtra; + HWVertexBuffer *VertexBuffer = nullptr; + FBVERTEX *VertexData = nullptr; + HWIndexBuffer *IndexBuffer = nullptr; + uint16_t *IndexData = nullptr; + BufferedTris *QuadExtra = nullptr; int VertexPos; int IndexPos; int QuadBatchPos; enum { BATCH_None, BATCH_Quads, BATCH_Lines } BatchType; HWPixelShader *Shaders[NUM_SHADERS]; - HWPixelShader *GammaShader; + HWPixelShader *GammaShader = nullptr; - HWSurface *BlockSurface[2]; - HWSurface *OldRenderTarget; - HWTexture *InitialWipeScreen, *FinalWipeScreen; + HWTexture *InitialWipeScreen = nullptr, *FinalWipeScreen = nullptr; class Wiper { diff --git a/src/win32/hardware.cpp b/src/win32/hardware.cpp index 787c0a4f3..2065a9072 100644 --- a/src/win32/hardware.cpp +++ b/src/win32/hardware.cpp @@ -137,8 +137,9 @@ void I_InitGraphics () ticker.SetGenericRepDefault (val, CVAR_Bool); //currentrenderer = vid_renderer; - if (currentrenderer==1) Video = gl_CreateVideo(); - else Video = new Win32Video (0); + Video = gl_CreateVideo(); + //if (currentrenderer==1) Video = gl_CreateVideo(); + //else Video = new Win32Video (0); if (Video == NULL) I_FatalError ("Failed to initialize display"); diff --git a/src/win32/win32gliface.cpp b/src/win32/win32gliface.cpp index 5d68d8427..14d49c69b 100644 --- a/src/win32/win32gliface.cpp +++ b/src/win32/win32gliface.cpp @@ -25,6 +25,7 @@ #include "gl/renderer/gl_renderer.h" #include "gl/system/gl_framebuffer.h" +#include "gl/system/gl_swframebuffer.h" extern "C" { _declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; @@ -386,7 +387,10 @@ DFrameBuffer *Win32GLVideo::CreateFrameBuffer(int width, int height, bool fs, DF //old->GetFlash(flashColor, flashAmount); delete old; } - fb = new OpenGLFrameBuffer(m_hMonitor, m_DisplayWidth, m_DisplayHeight, m_DisplayBits, m_DisplayHz, fs); + if (vid_renderer == 1) + fb = new OpenGLFrameBuffer(m_hMonitor, m_DisplayWidth, m_DisplayHeight, m_DisplayBits, m_DisplayHz, fs); + else + fb = new OpenGLSWFrameBuffer(m_hMonitor, m_DisplayWidth, m_DisplayHeight, m_DisplayBits, m_DisplayHz, fs); return fb; } diff --git a/wadsrc/static/shaders/glsl/swshader.fp b/wadsrc/static/shaders/glsl/swshader.fp new file mode 100644 index 000000000..22364f460 --- /dev/null +++ b/wadsrc/static/shaders/glsl/swshader.fp @@ -0,0 +1,146 @@ + +in vec4 PixelColor0; +in vec4 PixelColor1; +in vec4 PixelTexCoord0; + +out vec4 FragColor; + +uniform sampler2D Image;// : register(s0); +uniform sampler2D Palette;// : register(s1); +uniform sampler2D NewScreen;// : register(s0); +uniform sampler2D Burn;// : register(s1); + +uniform vec4 Desaturation;// : register(c1); // { Desat, 1 - Desat } +uniform vec4 PaletteMod;// : register(c2); +uniform vec4 Weights;// : register(c6); // RGB->Gray weighting { 77/256.0, 143/256.0, 37/256.0, 1 } +uniform vec4 Gamma;// : register(c7); + +vec4 TextureLookup(vec2 tex_coord) +{ +#if PALTEX + float index = texture(Image, tex_coord).x; + index = index * PaletteMod.x + PaletteMod.y; + return texture(Palette, vec2(index, 0.5)); +#else + return texture(Image, tex_coord); +#endif +} + +vec4 Invert(vec4 rgb) +{ +#if INVERT + rgb.rgb = Weights.www - rgb.xyz; +#endif + return rgb; +} + +float Grayscale(vec4 rgb) +{ + return dot(rgb.rgb, Weights.rgb); +} + +vec4 SampleTexture(vec2 tex_coord) +{ + return Invert(TextureLookup(tex_coord)); +} + +// Normal color calculation for most drawing modes. + +vec4 NormalColor(vec2 tex_coord, vec4 Flash, vec4 InvFlash) +{ + return Flash + SampleTexture(tex_coord) * InvFlash; +} + +// Copy the red channel to the alpha channel. Pays no attention to palettes. + +vec4 RedToAlpha(vec2 tex_coord, vec4 Flash, vec4 InvFlash) +{ + vec4 color = Invert(texture(Image, tex_coord)); + color.a = color.r; + return Flash + color * InvFlash; +} + +// Just return the value of c0. + +vec4 VertexColor(vec4 color) +{ + return color; +} + +// Emulate one of the special colormaps. (Invulnerability, gold, etc.) + +vec4 SpecialColormap(vec2 tex_coord, vec4 start, vec4 end) +{ + vec4 color = SampleTexture(tex_coord); + vec4 range = end - start; + // We can't store values greater than 1.0 in a color register, so we multiply + // the final result by 2 and expect the caller to divide the start and end by 2. + color.rgb = 2 * (start + Grayscale(color) * range).rgb; + // Duplicate alpha semantics of NormalColor. + color.a = start.a + color.a * end.a; + return color; +} + +// In-game colormap effect: fade to a particular color and multiply by another, with +// optional desaturation of the original color. Desaturation is stored in c1. +// Fade level is packed int fade.a. Fade.rgb has been premultiplied by alpha. +// Overall alpha is in color.a. +vec4 InGameColormap(vec2 tex_coord, vec4 color, vec4 fade) +{ + vec4 rgb = SampleTexture(tex_coord); + + // Desaturate +#if DESAT + vec3 intensity; + intensity.rgb = vec3(Grayscale(rgb) * Desaturation.x); + rgb.rgb = intensity.rgb + rgb.rgb * Desaturation.y; +#endif + + // Fade + rgb.rgb = rgb.rgb * fade.aaa + fade.rgb; + + // Shade and Alpha + rgb = rgb * color; + + return rgb; +} + +// Windowed gamma correction. + +vec4 GammaCorrection(vec2 tex_coord) +{ + vec4 color = texture(Image, tex_coord); + color.rgb = pow(color.rgb, Gamma.rgb); + return color; +} + +// The burn wipe effect. + +vec4 BurnWipe(vec4 coord) +{ + vec4 color = texture(NewScreen, coord.xy); + vec4 alpha = texture(Burn, coord.zw); + color.a = alpha.r * 2; + return color; +} + +void main() +{ +#if defined(ENORMALCOLOR) + FragColor = NormalColor(PixelTexCoord0.xy, PixelColor0, PixelColor1); +#elif defined(EREDTOALPHA) + FragColor = RedToAlpha(PixelTexCoord0.xy, PixelColor0, PixelColor1); +#elif defined(EVERTEXCOLOR) + FragColor = VertexColor(PixelColor0); +#elif defined(ESPECIALCOLORMAP) + FragColor = SpecialColormap(PixelTexCoord0.xy, PixelColor0, PixelColor1); +#elif defined(EINGAMECOLORMAP) + FragColor = InGameColormap(PixelTexCoord0.xy, PixelColor0, PixelColor1); +#elif defined(EBURNWIPE) + FragColor = BurnWipe(PixelTexCoord0); +#elif defined(EGAMMACORRECTION) + FragColor = GammaCorrection(PixelTexCoord0.xy); +#else + #error Entry point define is missing +#endif +} diff --git a/wadsrc/static/shaders/glsl/swshader.vp b/wadsrc/static/shaders/glsl/swshader.vp new file mode 100644 index 000000000..4d00296cb --- /dev/null +++ b/wadsrc/static/shaders/glsl/swshader.vp @@ -0,0 +1,17 @@ + +in vec4 AttrPosition; +in vec4 AttrColor0; +in vec4 AttrColor1; +in vec4 AttrTexCoord0; + +out vec4 PixelColor0; +out vec4 PixelColor1; +out vec4 PixelTexCoord0; + +void main() +{ + gl_Position = AttrPosition; + PixelColor0 = AttrColor0; + PixelColor1 = AttrColor1; + PixelTexCoord0 = AttrTexCoord0; +} From 682b040b978f84367eb3aa559b8add3cd265fbbd Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Tue, 11 Oct 2016 00:03:46 +0200 Subject: [PATCH 4/7] OpenGL based software renderer hardware accel now works for most things --- src/gl/system/gl_swframebuffer.cpp | 84 ++++++++++++++++++++------ src/gl/system/gl_swframebuffer.h | 36 +++++++---- wadsrc/static/shaders/glsl/swshader.fp | 16 ++--- wadsrc/static/shaders/glsl/swshader.vp | 7 ++- 4 files changed, 99 insertions(+), 44 deletions(-) diff --git a/src/gl/system/gl_swframebuffer.cpp b/src/gl/system/gl_swframebuffer.cpp index 895508d8b..1ff48d13b 100644 --- a/src/gl/system/gl_swframebuffer.cpp +++ b/src/gl/system/gl_swframebuffer.cpp @@ -70,6 +70,7 @@ #include "gl/utility/gl_clock.h" #include "gl/utility/gl_templates.h" #include "gl/gl_functions.h" +#include "gl_debug.h" CVAR(Int, gl_showpacks, 0, 0) #ifndef WIN32 // Defined in fb_d3d9 for Windows @@ -131,6 +132,9 @@ OpenGLSWFrameBuffer::OpenGLSWFrameBuffer(void *hMonitor, int width, int height, gl_LoadExtensions(); Super::InitializeState(); + Debug = std::make_shared(); + Debug->Update(); + // SetVSync needs to be at the very top to workaround a bug in Nvidia's OpenGL driver. // If wglSwapIntervalEXT is called after glBindFramebuffer in a frame the setting is not changed! //SetVSync(vid_vsync); @@ -160,6 +164,7 @@ OpenGLSWFrameBuffer::OpenGLSWFrameBuffer(void *hMonitor, int width, int height, ScreenWipe = nullptr; InScene = false; QuadExtra = new BufferedTris[MAX_QUAD_BATCH]; + memset(QuadExtra, 0, sizeof(BufferedTris) * MAX_QUAD_BATCH); Atlases = nullptr; PixelDoubling = 0; @@ -288,10 +293,10 @@ bool OpenGLSWFrameBuffer::CreatePixelShader(FString vertexsrc, FString fragments if (status != GL_FALSE) { errorShader = shader->FragmentShader; glGetShaderiv(shader->FragmentShader, GL_COMPILE_STATUS, &status); } if (status == GL_FALSE) { - static char buffer[10000]; + /*static char buffer[10000]; GLsizei length = 0; buffer[0] = 0; - glGetShaderInfoLog(errorShader, 10000, &length, buffer); + glGetShaderInfoLog(errorShader, 10000, &length, buffer);*/ *outShader = nullptr; return false; @@ -307,10 +312,19 @@ bool OpenGLSWFrameBuffer::CreatePixelShader(FString vertexsrc, FString fragments *outShader = nullptr; return false; } - glBindAttribLocation(shader->Program, 0, "Position"); - glBindAttribLocation(shader->Program, 1, "Color0"); - glBindAttribLocation(shader->Program, 2, "Color1"); - glBindAttribLocation(shader->Program, 3, "TexCoord"); + glBindAttribLocation(shader->Program, 0, "AttrPosition"); + glBindAttribLocation(shader->Program, 1, "AttrColor0"); + glBindAttribLocation(shader->Program, 2, "AttrColor1"); + glBindAttribLocation(shader->Program, 3, "AttrTexCoord0"); + + shader->ConstantLocations[PSCONST_Desaturation] = glGetUniformLocation(shader->Program, "Desaturation"); + shader->ConstantLocations[PSCONST_PaletteMod] = glGetUniformLocation(shader->Program, "PaletteMod"); + shader->ConstantLocations[PSCONST_Weights] = glGetUniformLocation(shader->Program, "Weights"); + shader->ConstantLocations[PSCONST_Gamma] = glGetUniformLocation(shader->Program, "Gamma"); + shader->ImageLocation = glGetUniformLocation(shader->Program, "Image"); + shader->PaletteLocation = glGetUniformLocation(shader->Program, "Palette"); + shader->NewScreenLocation = glGetUniformLocation(shader->Program, "NewScreen"); + shader->BurnLocation = glGetUniformLocation(shader->Program, "Burn"); *outShader = shader.release(); return true; @@ -361,7 +375,7 @@ bool OpenGLSWFrameBuffer::CreateIndexBuffer(int size, HWIndexBuffer **outIndexBu return true; } -bool OpenGLSWFrameBuffer::CreateTexture(int width, int height, int levels, int format, HWTexture **outTexture) +bool OpenGLSWFrameBuffer::CreateTexture(const FString &name, int width, int height, int levels, int format, HWTexture **outTexture) { auto obj = std::make_unique(); @@ -389,6 +403,8 @@ bool OpenGLSWFrameBuffer::CreateTexture(int width, int height, int levels, int f glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + FGLDebug::LabelObject(GL_TEXTURE, obj->Texture, name); + glBindTexture(GL_TEXTURE_2D, oldBinding); *outTexture = obj.release(); @@ -401,15 +417,32 @@ void OpenGLSWFrameBuffer::SetGammaRamp(const GammaRamp *ramp) void OpenGLSWFrameBuffer::SetPixelShaderConstantF(int uniformIndex, const float *data, int vec4fcount) { - glUniform4fv(uniformIndex, vec4fcount, data); + assert(uniformIndex < 4 && vec4fcount == 1); // This emulation of d3d9 only works for very simple stuff + for (int i = 0; i < 4; i++) + ShaderConstants[uniformIndex * 4 + i] = data[i]; + if (CurrentShader && CurrentShader->ConstantLocations[uniformIndex] != -1) + glUniform4fv(CurrentShader->ConstantLocations[uniformIndex], vec4fcount, data); } void OpenGLSWFrameBuffer::SetHWPixelShader(HWPixelShader *shader) { - if (shader) - glUseProgram(shader->Program); - else - glUseProgram(0); + if (shader != CurrentShader) + { + if (shader) + { + glUseProgram(shader->Program); + for (int i = 0; i < 4; i++) + { + if (shader->ConstantLocations[i] != -1) + glUniform4fv(shader->ConstantLocations[i], 1, &ShaderConstants[i * 4]); + } + } + else + { + glUseProgram(0); + } + } + CurrentShader = shader; } void OpenGLSWFrameBuffer::SetStreamSource(HWVertexBuffer *vertexBuffer) @@ -430,6 +463,8 @@ void OpenGLSWFrameBuffer::SetIndices(HWIndexBuffer *indexBuffer) void OpenGLSWFrameBuffer::DrawTriangleFans(int count, const FBVERTEX *vertices) { + count = 2 + count; + GLint oldBinding = 0; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &oldBinding); @@ -500,7 +535,7 @@ void OpenGLSWFrameBuffer::DrawPoints(int count, const FBVERTEX *vertices) void OpenGLSWFrameBuffer::DrawLineList(int count) { - glDrawArrays(GL_LINES, 0, count); + glDrawArrays(GL_LINES, 0, count * 2); } void OpenGLSWFrameBuffer::DrawTriangleList(int minIndex, int numVertices, int startIndex, int primitiveCount) @@ -511,6 +546,8 @@ void OpenGLSWFrameBuffer::DrawTriangleList(int minIndex, int numVertices, int st void OpenGLSWFrameBuffer::Present() { SwapBuffers(); + glViewport(0, 0, GetClientWidth(), GetClientHeight()); + Debug->Update(); } //========================================================================== @@ -611,6 +648,13 @@ bool OpenGLSWFrameBuffer::LoadShaders() { break; } + + glUseProgram(Shaders[i]->Program); + if (Shaders[i]->ImageLocation != -1) glUniform1i(Shaders[i]->ImageLocation, 0); + if (Shaders[i]->PaletteLocation != -1) glUniform1i(Shaders[i]->PaletteLocation, 1); + if (Shaders[i]->NewScreenLocation != -1) glUniform1i(Shaders[i]->NewScreenLocation, 0); + if (Shaders[i]->BurnLocation != -1) glUniform1i(Shaders[i]->BurnLocation, 1); + glUseProgram(0); } if (i == NUM_SHADERS) { // Success! @@ -711,7 +755,7 @@ void OpenGLSWFrameBuffer::KillNativeTexs() bool OpenGLSWFrameBuffer::CreateFBTexture() { - CreateTexture(Width, Height, 1, GL_R8, &FBTexture); + CreateTexture("FBTexture", Width, Height, 1, GL_R8, &FBTexture); FBWidth = Width; FBHeight = Height; return true; @@ -725,7 +769,7 @@ bool OpenGLSWFrameBuffer::CreateFBTexture() bool OpenGLSWFrameBuffer::CreatePaletteTexture() { - if (!CreateTexture(256, 1, 1, GL_RGBA8, &PaletteTexture)) + if (!CreateTexture("PaletteTexture", 256, 1, 1, GL_RGBA8, &PaletteTexture)) { return false; } @@ -1118,7 +1162,7 @@ void OpenGLSWFrameBuffer::Draw3DPart(bool copy3d) GLint oldBinding = 0; glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldBinding); glBindTexture(GL_TEXTURE_2D, FBTexture->Texture); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, Width, Height, GL_R8, GL_UNSIGNED_BYTE, 0); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, Width, Height, GL_RED, GL_UNSIGNED_BYTE, 0); glBindTexture(GL_TEXTURE_2D, oldBinding); } @@ -1623,7 +1667,7 @@ OpenGLSWFrameBuffer::Atlas::Atlas(OpenGLSWFrameBuffer *fb, int w, int h, int for } *prev = this; - fb->CreateTexture(w, h, 1, format, &Tex); + fb->CreateTexture("Atlas", w, h, 1, format, &Tex); Width = w; Height = h; } @@ -1907,7 +1951,7 @@ bool OpenGLSWFrameBuffer::OpenGLTex::Update() glBindTexture(GL_TEXTURE_2D, Box->Owner->Tex->Texture); glTexSubImage2D(GL_TEXTURE_2D, 0, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, format == GL_RGBA8 ? GL_BGRA : GL_RED, GL_UNSIGNED_BYTE, 0); glBindTexture(GL_TEXTURE_2D, oldBinding); - glBindTexture(GL_PIXEL_UNPACK_BUFFER, 0); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); return true; } @@ -1996,7 +2040,7 @@ OpenGLSWFrameBuffer::OpenGLPal::OpenGLPal(FRemapTable *remap, OpenGLSWFrameBuffe BorderColor = 0; RoundedPaletteSize = count; - if (fb->CreateTexture(count, 1, 1, GL_RGBA8, &Tex)) + if (fb->CreateTexture("Pal", count, 1, 1, GL_RGBA8, &Tex)) { if (!Update()) { @@ -3312,7 +3356,7 @@ void OpenGLSWFrameBuffer::SetColorOverlay(uint32_t color, float alpha, uint32_t } } -void OpenGLSWFrameBuffer::EnableAlphaTest(BOOL enabled) +void OpenGLSWFrameBuffer::EnableAlphaTest(bool enabled) { if (enabled != AlphaTestEnabled) { diff --git a/src/gl/system/gl_swframebuffer.h b/src/gl/system/gl_swframebuffer.h index eed9b6f7f..2c50b2e97 100644 --- a/src/gl/system/gl_swframebuffer.h +++ b/src/gl/system/gl_swframebuffer.h @@ -136,12 +136,18 @@ private: int Program = 0; int VertexShader = 0; int FragmentShader = 0; + + int ConstantLocations[4]; + int ImageLocation = -1; + int PaletteLocation = -1; + int NewScreenLocation = -1; + int BurnLocation = -1; }; bool CreatePixelShader(FString vertexsrc, FString fragmentsrc, const FString &defines, HWPixelShader **outShader); bool CreateVertexBuffer(int size, HWVertexBuffer **outVertexBuffer); bool CreateIndexBuffer(int size, HWIndexBuffer **outIndexBuffer); - bool CreateTexture(int width, int height, int levels, int format, HWTexture **outTexture); + bool CreateTexture(const FString &name, int width, int height, int levels, int format, HWTexture **outTexture); void SetGammaRamp(const GammaRamp *ramp); void SetPixelShaderConstantF(int uniformIndex, const float *data, int vec4fcount); void SetHWPixelShader(HWPixelShader *shader); @@ -154,7 +160,7 @@ private: void Present(); static uint32_t ColorARGB(uint32_t a, uint32_t r, uint32_t g, uint32_t b) { return ((a & 0xff) << 24) | ((r & 0xff) << 16) | ((g & 0xff) << 8) | ((b) & 0xff); } - static uint32_t ColorRGBA(uint32_t a, uint32_t r, uint32_t g, uint32_t b) { return ColorARGB(a, r, g, b); } + static uint32_t ColorRGBA(uint32_t r, uint32_t g, uint32_t b, uint32_t a) { return ColorARGB(a, r, g, b); } static uint32_t ColorXRGB(uint32_t r, uint32_t g, uint32_t b) { return ColorARGB(0xff, r, g, b); } static uint32_t ColorValue(float r, float g, float b, float a) { return ColorRGBA((uint32_t)(r * 255.0f), (uint32_t)(g * 255.0f), (uint32_t)(b * 255.0f), (uint32_t)(a * 255.0f)); } @@ -297,10 +303,10 @@ private: enum { - PSCONST_Desaturation = 1, - PSCONST_PaletteMod = 2, - PSCONST_Weights = 6, - PSCONST_Gamma = 7, + PSCONST_Desaturation = 0, + PSCONST_PaletteMod = 1, + PSCONST_Weights = 2, + PSCONST_Gamma = 3, }; enum { @@ -366,7 +372,7 @@ private: void EndBatch(); // State - void EnableAlphaTest(BOOL enabled); + void EnableAlphaTest(bool enabled); void SetAlphaBlend(int op, int srcblend = 0, int destblend = 0); void SetConstant(int cnum, float r, float g, float b, float a); void SetPixelShader(HWPixelShader *shader); @@ -377,13 +383,17 @@ private: template static void SafeRelease(T &x) { if (x != nullptr) { delete x; x = nullptr; } } - std::unique_ptr StreamVertexBuffer; + std::shared_ptr Debug; - BOOL AlphaTestEnabled; - BOOL AlphaBlendEnabled; - int AlphaBlendOp; - int AlphaSrcBlend; - int AlphaDestBlend; + std::unique_ptr StreamVertexBuffer; + float ShaderConstants[16]; + HWPixelShader *CurrentShader = nullptr; + + bool AlphaTestEnabled = false; + bool AlphaBlendEnabled = false; + int AlphaBlendOp = 0; + int AlphaSrcBlend = 0; + int AlphaDestBlend = 0; float Constant[3][4]; uint32_t CurBorderColor; HWPixelShader *CurPixelShader; diff --git a/wadsrc/static/shaders/glsl/swshader.fp b/wadsrc/static/shaders/glsl/swshader.fp index 22364f460..639ea92e5 100644 --- a/wadsrc/static/shaders/glsl/swshader.fp +++ b/wadsrc/static/shaders/glsl/swshader.fp @@ -5,15 +5,15 @@ in vec4 PixelTexCoord0; out vec4 FragColor; -uniform sampler2D Image;// : register(s0); -uniform sampler2D Palette;// : register(s1); -uniform sampler2D NewScreen;// : register(s0); -uniform sampler2D Burn;// : register(s1); +uniform sampler2D Image; +uniform sampler2D Palette; +uniform sampler2D NewScreen; +uniform sampler2D Burn; -uniform vec4 Desaturation;// : register(c1); // { Desat, 1 - Desat } -uniform vec4 PaletteMod;// : register(c2); -uniform vec4 Weights;// : register(c6); // RGB->Gray weighting { 77/256.0, 143/256.0, 37/256.0, 1 } -uniform vec4 Gamma;// : register(c7); +uniform vec4 Desaturation; // { Desat, 1 - Desat } +uniform vec4 PaletteMod; +uniform vec4 Weights; // RGB->Gray weighting { 77/256.0, 143/256.0, 37/256.0, 1 } +uniform vec4 Gamma; vec4 TextureLookup(vec2 tex_coord) { diff --git a/wadsrc/static/shaders/glsl/swshader.vp b/wadsrc/static/shaders/glsl/swshader.vp index 4d00296cb..35254a042 100644 --- a/wadsrc/static/shaders/glsl/swshader.vp +++ b/wadsrc/static/shaders/glsl/swshader.vp @@ -10,8 +10,9 @@ out vec4 PixelTexCoord0; void main() { - gl_Position = AttrPosition; - PixelColor0 = AttrColor0; - PixelColor1 = AttrColor1; + gl_Position = vec4(AttrPosition.xy / vec2(1920*2,1080*2) * 2.0 - 1.0, 1.0, 1.0); + gl_Position.y = -gl_Position.y; + PixelColor0 = AttrColor0.bgra; + PixelColor1 = AttrColor1.bgra; PixelTexCoord0 = AttrTexCoord0; } From 01dc9de8d15cf5ea43b5742673fac926e4bd5223 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Tue, 11 Oct 2016 10:27:18 +0200 Subject: [PATCH 5/7] Misc bug fixes and performance improvements --- src/gl/system/gl_swframebuffer.cpp | 112 +++++++++++++++---------- src/gl/system/gl_swframebuffer.h | 59 ++++++++----- src/win32/hardware.cpp | 10 ++- wadsrc/static/shaders/glsl/swshader.vp | 4 +- 4 files changed, 113 insertions(+), 72 deletions(-) diff --git a/src/gl/system/gl_swframebuffer.cpp b/src/gl/system/gl_swframebuffer.cpp index 1ff48d13b..d2f2e3aee 100644 --- a/src/gl/system/gl_swframebuffer.cpp +++ b/src/gl/system/gl_swframebuffer.cpp @@ -130,14 +130,14 @@ OpenGLSWFrameBuffer::OpenGLSWFrameBuffer(void *hMonitor, int width, int height, ogl_LoadFunctions(); } gl_LoadExtensions(); - Super::InitializeState(); - - Debug = std::make_shared(); - Debug->Update(); + InitializeState(); // SetVSync needs to be at the very top to workaround a bug in Nvidia's OpenGL driver. // If wglSwapIntervalEXT is called after glBindFramebuffer in a frame the setting is not changed! - //SetVSync(vid_vsync); + Super::SetVSync(vid_vsync); + + Debug = std::make_shared(); + Debug->Update(); VertexBuffer = nullptr; IndexBuffer = nullptr; @@ -216,7 +216,7 @@ OpenGLSWFrameBuffer::~OpenGLSWFrameBuffer() OpenGLSWFrameBuffer::HWTexture::~HWTexture() { if (Texture != 0) glDeleteTextures(1, (GLuint*)&Texture); - if (Buffer != 0) glDeleteBuffers(1, (GLuint*)&Buffer); + if (Buffers[0] != 0) glDeleteBuffers(2, (GLuint*)Buffers); } OpenGLSWFrameBuffer::HWVertexBuffer::~HWVertexBuffer() @@ -228,7 +228,7 @@ OpenGLSWFrameBuffer::HWVertexBuffer::~HWVertexBuffer() OpenGLSWFrameBuffer::FBVERTEX *OpenGLSWFrameBuffer::HWVertexBuffer::Lock() { glBindBuffer(GL_ARRAY_BUFFER, Buffer); - return (FBVERTEX*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + return (FBVERTEX*)glMapBufferRange(GL_ARRAY_BUFFER, 0, Size, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); } void OpenGLSWFrameBuffer::HWVertexBuffer::Unlock() @@ -246,7 +246,7 @@ uint16_t *OpenGLSWFrameBuffer::HWIndexBuffer::Lock() { glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &LockedOldBinding); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Buffer); - return (uint16_t*)glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY); + return (uint16_t*)glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, Size, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); } void OpenGLSWFrameBuffer::HWIndexBuffer::Unlock() @@ -321,6 +321,7 @@ bool OpenGLSWFrameBuffer::CreatePixelShader(FString vertexsrc, FString fragments shader->ConstantLocations[PSCONST_PaletteMod] = glGetUniformLocation(shader->Program, "PaletteMod"); shader->ConstantLocations[PSCONST_Weights] = glGetUniformLocation(shader->Program, "Weights"); shader->ConstantLocations[PSCONST_Gamma] = glGetUniformLocation(shader->Program, "Gamma"); + shader->ConstantLocations[PSCONST_ScreenSize] = glGetUniformLocation(shader->Program, "ScreenSize"); shader->ImageLocation = glGetUniformLocation(shader->Program, "Image"); shader->PaletteLocation = glGetUniformLocation(shader->Program, "Palette"); shader->NewScreenLocation = glGetUniformLocation(shader->Program, "NewScreen"); @@ -334,6 +335,8 @@ bool OpenGLSWFrameBuffer::CreateVertexBuffer(int size, HWVertexBuffer **outVerte { auto obj = std::make_unique(); + obj->Size = size; + GLint oldBinding = 0; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &oldBinding); @@ -341,6 +344,7 @@ bool OpenGLSWFrameBuffer::CreateVertexBuffer(int size, HWVertexBuffer **outVerte glGenBuffers(1, (GLuint*)&obj->Buffer); glBindVertexArray(obj->VertexArray); glBindBuffer(GL_ARRAY_BUFFER, obj->Buffer); + FGLDebug::LabelObject(GL_BUFFER, obj->Buffer, "VertexBuffer"); glBufferData(GL_ARRAY_BUFFER, size, nullptr, GL_STREAM_DRAW); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); @@ -362,11 +366,14 @@ bool OpenGLSWFrameBuffer::CreateIndexBuffer(int size, HWIndexBuffer **outIndexBu { auto obj = std::make_unique(); + obj->Size = size; + GLint oldBinding = 0; glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, &oldBinding); glGenBuffers(1, (GLuint*)&obj->Buffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, obj->Buffer); + FGLDebug::LabelObject(GL_BUFFER, obj->Buffer, "IndexBuffer"); glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, nullptr, GL_STREAM_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, oldBinding); @@ -417,7 +424,7 @@ void OpenGLSWFrameBuffer::SetGammaRamp(const GammaRamp *ramp) void OpenGLSWFrameBuffer::SetPixelShaderConstantF(int uniformIndex, const float *data, int vec4fcount) { - assert(uniformIndex < 4 && vec4fcount == 1); // This emulation of d3d9 only works for very simple stuff + assert(uniformIndex < NumPSCONST && vec4fcount == 1); // This emulation of d3d9 only works for very simple stuff for (int i = 0; i < 4; i++) ShaderConstants[uniformIndex * 4 + i] = data[i]; if (CurrentShader && CurrentShader->ConstantLocations[uniformIndex] != -1) @@ -431,7 +438,7 @@ void OpenGLSWFrameBuffer::SetHWPixelShader(HWPixelShader *shader) if (shader) { glUseProgram(shader->Program); - for (int i = 0; i < 4; i++) + for (int i = 0; i < NumPSCONST; i++) { if (shader->ConstantLocations[i] != -1) glUniform4fv(shader->ConstantLocations[i], 1, &ShaderConstants[i * 4]); @@ -547,6 +554,8 @@ void OpenGLSWFrameBuffer::Present() { SwapBuffers(); glViewport(0, 0, GetClientWidth(), GetClientHeight()); + float screensize[4] = { (float)GetClientWidth(), (float)GetClientHeight(), 1.0f, 1.0f }; + SetPixelShaderConstantF(PSCONST_ScreenSize, screensize, 1); Debug->Update(); } @@ -561,7 +570,7 @@ void OpenGLSWFrameBuffer::Present() void OpenGLSWFrameBuffer::SetInitialState() { - AlphaBlendEnabled = FALSE; + AlphaBlendEnabled = false; AlphaBlendOp = GL_FUNC_ADD; AlphaSrcBlend = 0; AlphaDestBlend = 0; @@ -583,7 +592,10 @@ void OpenGLSWFrameBuffer::SetInitialState() float weights[4] = { 77 / 256.f, 143 / 256.f, 37 / 256.f, 1 }; SetPixelShaderConstantF(PSCONST_Weights, weights, 1); - AlphaTestEnabled = FALSE; + float screensize[4] = { (float)GetClientWidth(), (float)GetClientHeight(), 1.0f, 1.0f }; + SetPixelShaderConstantF(PSCONST_ScreenSize, screensize, 1); + + AlphaTestEnabled = false; CurBorderColor = 0; @@ -1043,6 +1055,7 @@ void OpenGLSWFrameBuffer::Update() if (NeedPalUpdate) { UploadPalette(); + NeedPalUpdate = false; } BlitCycles.Reset(); @@ -1075,11 +1088,6 @@ void OpenGLSWFrameBuffer::Flip() DrawLetterbox(); DoWindowedGamma(); - // Limiting the frame rate is as simple as waiting for the timer to signal this event. - if (FPSLimitEvent != nullptr) - { - WaitForSingleObject(FPSLimitEvent, 1000); - } Present(); InScene = false; @@ -1130,18 +1138,21 @@ void OpenGLSWFrameBuffer::Draw3DPart(bool copy3d) { if (copy3d) { - if (FBTexture->Buffer == 0) + if (FBTexture->Buffers[0] == 0) { - glGenBuffers(1, (GLuint*)&FBTexture->Buffer); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, FBTexture->Buffer); + glGenBuffers(2, (GLuint*)FBTexture->Buffers); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, FBTexture->Buffers[0]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, Width * Height, nullptr, GL_STREAM_DRAW); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, FBTexture->Buffers[1]); glBufferData(GL_PIXEL_UNPACK_BUFFER, Width * Height, nullptr, GL_STREAM_DRAW); } else { - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, FBTexture->Buffer); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, FBTexture->Buffers[FBTexture->CurrentBuffer]); + FBTexture->CurrentBuffer = (FBTexture->CurrentBuffer + 1) & 1; } - uint8_t *dest = (uint8_t*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); + uint8_t *dest = (uint8_t*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, Width * Height, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); if (dest) { if (Pitch == Width) @@ -1178,7 +1189,7 @@ void OpenGLSWFrameBuffer::Draw3DPart(bool copy3d) SetPaletteTexture(PaletteTexture, 256, BorderColor); memset(Constant, 0, sizeof(Constant)); SetAlphaBlend(0); - EnableAlphaTest(FALSE); + EnableAlphaTest(false); SetPixelShader(Shaders[SHADER_NormalColorPal]); if (copy3d) { @@ -1252,7 +1263,7 @@ void OpenGLSWFrameBuffer::DoWindowedGamma() SetTexture(0, TempRenderTexture); SetPixelShader(Windowed && GammaShader ? GammaShader : Shaders[SHADER_NormalColor]); SetAlphaBlend(0); - EnableAlphaTest(FALSE); + EnableAlphaTest(false); DrawTriangleFans(2, verts); delete OldRenderTarget; OldRenderTarget = nullptr; @@ -1261,18 +1272,21 @@ void OpenGLSWFrameBuffer::DoWindowedGamma() void OpenGLSWFrameBuffer::UploadPalette() { - if (PaletteTexture->Buffer == 0) + if (PaletteTexture->Buffers[0] == 0) { - glGenBuffers(1, (GLuint*)&PaletteTexture->Buffer); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, PaletteTexture->Buffer); + glGenBuffers(2, (GLuint*)PaletteTexture->Buffers); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, PaletteTexture->Buffers[0]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, 256 * 4, nullptr, GL_STREAM_DRAW); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, PaletteTexture->Buffers[1]); glBufferData(GL_PIXEL_UNPACK_BUFFER, 256 * 4, nullptr, GL_STREAM_DRAW); } else { - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, PaletteTexture->Buffer); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, PaletteTexture->Buffers[PaletteTexture->CurrentBuffer]); + PaletteTexture->CurrentBuffer = (PaletteTexture->CurrentBuffer + 1) & 1; } - uint8_t *pix = (uint8_t *)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); + uint8_t *pix = (uint8_t*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, 256 * 4, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); if (pix) { int i; @@ -1358,6 +1372,7 @@ void OpenGLSWFrameBuffer::SetVSync(bool vsync) VSync = vsync; Reset(); } + Super::SetVSync(vsync); } void OpenGLSWFrameBuffer::NewRefreshRate() @@ -1510,7 +1525,7 @@ void OpenGLSWFrameBuffer::DrawPackedTextures(int packnum) BufferedTris *quad = &QuadExtra[QuadBatchPos]; FBVERTEX *vert = &VertexData[VertexPos]; - quad->Group1 = 0; + quad->ClearSetup(); if (pack->Format == GL_R8/* && !tex->IsGray*/) { quad->Flags = BQF_WrapUV | BQF_GamePalette/* | BQF_DisableAlphaTest*/; @@ -1885,8 +1900,8 @@ bool OpenGLSWFrameBuffer::OpenGLTex::Update() rect = Box->Area; - if (Box->Owner->Tex->Buffer == 0) - glGenBuffers(1, (GLuint*)&Box->Owner->Tex->Buffer); + if (Box->Owner->Tex->Buffers[0] == 0) + glGenBuffers(2, (GLuint*)Box->Owner->Tex->Buffers); int bytesPerPixel = 4; switch (format) @@ -1896,11 +1911,13 @@ bool OpenGLSWFrameBuffer::OpenGLTex::Update() default: return false; } - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, Box->Owner->Tex->Buffer); - glBufferData(GL_PIXEL_UNPACK_BUFFER, (rect.right - rect.left) * (rect.bottom - rect.top) * bytesPerPixel, nullptr, GL_STREAM_DRAW); + int buffersize = (rect.right - rect.left) * (rect.bottom - rect.top) * bytesPerPixel; + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, Box->Owner->Tex->Buffers[Box->Owner->Tex->CurrentBuffer]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, buffersize, nullptr, GL_STREAM_DRAW); + Box->Owner->Tex->CurrentBuffer = (Box->Owner->Tex->CurrentBuffer + 1) & 1; int pitch = (rect.right - rect.left) * bytesPerPixel; - uint8_t *bits = (uint8_t *)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); + uint8_t *bits = (uint8_t *)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, buffersize, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); dest = bits; if (!dest) { @@ -2088,18 +2105,21 @@ bool OpenGLSWFrameBuffer::OpenGLPal::Update() assert(Tex != nullptr); - if (Tex->Buffer == 0) + if (Tex->Buffers[0] == 0) { - glGenBuffers(1, (GLuint*)&Tex->Buffer); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, Tex->Buffer); + glGenBuffers(2, (GLuint*)Tex->Buffers); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, Tex->Buffers[0]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, Remap->NumEntries * 4, nullptr, GL_STREAM_DRAW); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, Tex->Buffers[1]); glBufferData(GL_PIXEL_UNPACK_BUFFER, Remap->NumEntries * 4, nullptr, GL_STREAM_DRAW); } else { - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, Tex->Buffer); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, Tex->Buffers[Tex->CurrentBuffer]); + Tex->CurrentBuffer = (Tex->CurrentBuffer + 1) & 1; } - buff = (uint32_t *)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); + buff = (uint32_t *)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, Remap->NumEntries * 4, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); if (buff == nullptr) { return false; @@ -2625,7 +2645,7 @@ void OpenGLSWFrameBuffer::FlatFill(int left, int top, int right, int bottom, FTe BufferedTris *quad = &QuadExtra[QuadBatchPos]; FBVERTEX *vert = &VertexData[VertexPos]; - quad->Group1 = 0; + quad->ClearSetup(); if (tex->GetTexFormat() == GL_R8 && !tex->IsGray) { quad->Flags = BQF_WrapUV | BQF_GamePalette; // | BQF_DisableAlphaTest; @@ -2743,7 +2763,7 @@ void OpenGLSWFrameBuffer::FillSimplePoly(FTexture *texture, FVector2 *points, in color0 = 0; color1 = 0xFFFFFFFF; - quad->Group1 = 0; + quad->ClearSetup(); if (tex->GetTexFormat() == GL_R8 && !tex->IsGray) { quad->Flags = BQF_WrapUV | BQF_GamePalette | BQF_DisableAlphaTest; @@ -2830,7 +2850,7 @@ void OpenGLSWFrameBuffer::AddColorOnlyQuad(int left, int top, int width, int hei float x = float(left) - 0.5f; float y = float(top) - 0.5f + (GatheringWipeScreen ? 0 : LBOffset); - quad->Group1 = 0; + quad->ClearSetup(); quad->ShaderNum = BQS_ColorOnly; if ((color & 0xFF000000) != 0xFF000000) { @@ -3004,7 +3024,7 @@ void OpenGLSWFrameBuffer::EndQuadBatch() { const BufferedTris *q2 = &QuadExtra[j]; if (quad->Texture != q2->Texture || - quad->Group1 != q2->Group1 || + !quad->IsSameSetup(*q2) || quad->Palette != q2->Palette) { break; @@ -3371,7 +3391,7 @@ void OpenGLSWFrameBuffer::SetAlphaBlend(int op, int srcblend, int destblend) { // Disable alpha blend if (AlphaBlendEnabled) { - AlphaBlendEnabled = FALSE; + AlphaBlendEnabled = false; glDisable(GL_BLEND); } } @@ -3382,7 +3402,7 @@ void OpenGLSWFrameBuffer::SetAlphaBlend(int op, int srcblend, int destblend) if (!AlphaBlendEnabled) { - AlphaBlendEnabled = TRUE; + AlphaBlendEnabled = true; glEnable(GL_BLEND); } if (AlphaBlendOp != op) diff --git a/src/gl/system/gl_swframebuffer.h b/src/gl/system/gl_swframebuffer.h index 2c50b2e97..fca5c0839 100644 --- a/src/gl/system/gl_swframebuffer.h +++ b/src/gl/system/gl_swframebuffer.h @@ -78,7 +78,16 @@ private: uint32_t color0, color1; FLOAT tu, tv; }; - //#define D3DFVF_FBVERTEX (D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_SPECULAR | D3DFVF_TEX1) + + enum + { + PSCONST_Desaturation, + PSCONST_PaletteMod, + PSCONST_Weights, + PSCONST_Gamma, + PSCONST_ScreenSize, + NumPSCONST + }; struct GammaRamp { @@ -93,10 +102,12 @@ private: class HWTexture { public: + HWTexture() { Buffers[0] = 0; Buffers[1] = 0; } ~HWTexture(); int Texture = 0; - int Buffer = 0; + int Buffers[2]; + int CurrentBuffer = 0; int WrapS = 0; int WrapT = 0; int Format = 0; @@ -112,6 +123,7 @@ private: int VertexArray = 0; int Buffer = 0; + int Size = 0; }; class HWIndexBuffer @@ -123,6 +135,7 @@ private: void Unlock(); int Buffer = 0; + int Size = 0; private: int LockedOldBinding = 0; @@ -137,7 +150,7 @@ private: int VertexShader = 0; int FragmentShader = 0; - int ConstantLocations[4]; + int ConstantLocations[NumPSCONST]; int ImageLocation = -1; int PaletteLocation = -1; int NewScreenLocation = -1; @@ -283,31 +296,33 @@ private: struct BufferedTris { - union - { - struct - { - uint8_t Flags; - uint8_t ShaderNum : 4; - int BlendOp; - int SrcBlend, DestBlend; - }; - uint32_t Group1; - }; + uint8_t Flags; + uint8_t ShaderNum; + int BlendOp; + int SrcBlend; + int DestBlend; + uint8_t Desat; OpenGLPal *Palette; HWTexture *Texture; uint16_t NumVerts; // Number of _unique_ vertices used by this set. uint16_t NumTris; // Number of triangles used by this set. + + void ClearSetup() + { + Flags = 0; + ShaderNum = 0; + BlendOp = 0; + SrcBlend = 0; + DestBlend = 0; + } + + bool IsSameSetup(const BufferedTris &other) const + { + return Flags == other.Flags && ShaderNum == other.ShaderNum && BlendOp == other.BlendOp && SrcBlend == other.SrcBlend && DestBlend == other.DestBlend; + } }; - enum - { - PSCONST_Desaturation = 0, - PSCONST_PaletteMod = 1, - PSCONST_Weights = 2, - PSCONST_Gamma = 3, - }; enum { SHADER_NormalColor, @@ -386,7 +401,7 @@ private: std::shared_ptr Debug; std::unique_ptr StreamVertexBuffer; - float ShaderConstants[16]; + float ShaderConstants[NumPSCONST * 4]; HWPixelShader *CurrentShader = nullptr; bool AlphaTestEnabled = false; diff --git a/src/win32/hardware.cpp b/src/win32/hardware.cpp index 2065a9072..1d6e65477 100644 --- a/src/win32/hardware.cpp +++ b/src/win32/hardware.cpp @@ -136,10 +136,14 @@ void I_InitGraphics () val.Bool = !!Args->CheckParm ("-devparm"); ticker.SetGenericRepDefault (val, CVAR_Bool); - //currentrenderer = vid_renderer; +//#define USE_D3D9_VIDEO +#ifdef USE_D3D9_VIDEO + if (currentrenderer == 1) Video = gl_CreateVideo(); + else Video = new Win32Video(0); +#else + currentrenderer = vid_renderer; Video = gl_CreateVideo(); - //if (currentrenderer==1) Video = gl_CreateVideo(); - //else Video = new Win32Video (0); +#endif if (Video == NULL) I_FatalError ("Failed to initialize display"); diff --git a/wadsrc/static/shaders/glsl/swshader.vp b/wadsrc/static/shaders/glsl/swshader.vp index 35254a042..a95be1e1a 100644 --- a/wadsrc/static/shaders/glsl/swshader.vp +++ b/wadsrc/static/shaders/glsl/swshader.vp @@ -8,9 +8,11 @@ out vec4 PixelColor0; out vec4 PixelColor1; out vec4 PixelTexCoord0; +uniform vec4 ScreenSize; + void main() { - gl_Position = vec4(AttrPosition.xy / vec2(1920*2,1080*2) * 2.0 - 1.0, 1.0, 1.0); + gl_Position = vec4(AttrPosition.xy / ScreenSize.xy * 2.0 - 1.0, 1.0, 1.0); gl_Position.y = -gl_Position.y; PixelColor0 = AttrColor0.bgra; PixelColor1 = AttrColor1.bgra; From 7911302ad85d204a8e286ce2282665e5474a9f33 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Tue, 11 Oct 2016 13:09:32 +0200 Subject: [PATCH 6/7] Hook in gamma shader --- src/gl/system/gl_swframebuffer.cpp | 104 ++++++++++++++++++++--------- src/gl/system/gl_swframebuffer.h | 15 ++++- src/win32/hardware.cpp | 1 - 3 files changed, 86 insertions(+), 34 deletions(-) diff --git a/src/gl/system/gl_swframebuffer.cpp b/src/gl/system/gl_swframebuffer.cpp index d2f2e3aee..4f8c25742 100644 --- a/src/gl/system/gl_swframebuffer.cpp +++ b/src/gl/system/gl_swframebuffer.cpp @@ -150,7 +150,6 @@ OpenGLSWFrameBuffer::OpenGLSWFrameBuffer(void *hMonitor, int width, int height, { Shaders[i] = nullptr; } - GammaShader = nullptr; VSync = vid_vsync; BlendingRect.left = 0; BlendingRect.top = 0; @@ -213,6 +212,12 @@ OpenGLSWFrameBuffer::~OpenGLSWFrameBuffer() delete[] QuadExtra; } +OpenGLSWFrameBuffer::HWFrameBuffer::~HWFrameBuffer() +{ + if (Framebuffer != 0) glDeleteFramebuffers(1, (GLuint*)&Framebuffer); + delete Texture; +} + OpenGLSWFrameBuffer::HWTexture::~HWTexture() { if (Texture != 0) glDeleteTextures(1, (GLuint*)&Texture); @@ -262,6 +267,43 @@ OpenGLSWFrameBuffer::HWPixelShader::~HWPixelShader() if (FragmentShader != 0) glDeleteShader(FragmentShader); } +bool OpenGLSWFrameBuffer::CreateFrameBuffer(const FString &name, int width, int height, HWFrameBuffer **outFramebuffer) +{ + auto fb = std::make_unique(); + + if (!CreateTexture(name, width, height, 1, GL_RGBA16F, &fb->Texture)) + { + outFramebuffer = nullptr; + return false; + } + + glGenFramebuffers(1, (GLuint*)&fb->Framebuffer); + + GLint oldFramebufferBinding = 0, oldTextureBinding = 0; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, &oldFramebufferBinding); + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldTextureBinding); + + glBindFramebuffer(GL_FRAMEBUFFER, fb->Framebuffer); + FGLDebug::LabelObject(GL_FRAMEBUFFER, fb->Framebuffer, name); + + glBindTexture(GL_TEXTURE_2D, fb->Texture->Texture); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fb->Texture->Texture, 0); + + GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + glBindFramebuffer(GL_FRAMEBUFFER, oldFramebufferBinding); + glBindTexture(GL_TEXTURE_2D, oldTextureBinding); + + if (result != GL_FRAMEBUFFER_COMPLETE) + { + outFramebuffer = nullptr; + return false; + } + + *outFramebuffer = fb.release(); + return true; +} + bool OpenGLSWFrameBuffer::CreatePixelShader(FString vertexsrc, FString fragmentsrc, const FString &defines, HWPixelShader **outShader) { auto shader = std::make_unique(); @@ -398,6 +440,7 @@ bool OpenGLSWFrameBuffer::CreateTexture(const FString &name, int width, int heig { case GL_R8: srcformat = GL_RED; break; case GL_RGBA8: srcformat = GL_BGRA; break; + case GL_RGBA16F: srcformat = GL_RGBA; break; case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: srcformat = GL_RGB; break; case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: srcformat = GL_RGBA; break; case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: srcformat = GL_RGBA; break; @@ -552,11 +595,28 @@ void OpenGLSWFrameBuffer::DrawTriangleList(int minIndex, int numVertices, int st void OpenGLSWFrameBuffer::Present() { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + FBVERTEX verts[4]; + + CalcFullscreenCoords(verts, false, true, 0, 0xFFFFFFFF); + for (int i = 0; i < 4; i++) + verts[i].tv = 1.0f - verts[i].tv; + SetTexture(0, OutputFB->Texture); + SetPixelShader(Shaders[SHADER_GammaCorrection]); + SetAlphaBlend(0); + EnableAlphaTest(false); + DrawTriangleFans(2, verts); + SwapBuffers(); + Debug->Update(); + glViewport(0, 0, GetClientWidth(), GetClientHeight()); + float screensize[4] = { (float)GetClientWidth(), (float)GetClientHeight(), 1.0f, 1.0f }; SetPixelShaderConstantF(PSCONST_ScreenSize, screensize, 1); - Debug->Update(); + + glBindFramebuffer(GL_FRAMEBUFFER, OutputFB->Framebuffer); } //========================================================================== @@ -617,6 +677,11 @@ bool OpenGLSWFrameBuffer::CreateResources() { return false; } + + if (!CreateFrameBuffer("OutputFB", Width, Height, &OutputFB)) + return false; + glBindFramebuffer(GL_FRAMEBUFFER, OutputFB->Framebuffer); + if (!CreateFBTexture() || !CreatePaletteTexture()) { @@ -698,7 +763,6 @@ void OpenGLSWFrameBuffer::ReleaseResources() { SafeRelease(Shaders[i]); } - GammaShader = nullptr; if (ScreenWipe != nullptr) { delete ScreenWipe; @@ -720,11 +784,17 @@ void OpenGLSWFrameBuffer::ReleaseDefaultPoolItems() SafeRelease(InitialWipeScreen); SafeRelease(VertexBuffer); SafeRelease(IndexBuffer); + SafeRelease(OutputFB); } bool OpenGLSWFrameBuffer::Reset() { ReleaseDefaultPoolItems(); + + if (!CreateFrameBuffer("OutputFB", Width, Height, &OutputFB)) + return false; + glBindFramebuffer(GL_FRAMEBUFFER, OutputFB->Framebuffer); + if (!CreateFBTexture() || !CreateVertexes()) { return false; @@ -1086,7 +1156,6 @@ void OpenGLSWFrameBuffer::Flip() assert(InScene); DrawLetterbox(); - DoWindowedGamma(); Present(); InScene = false; @@ -1243,33 +1312,6 @@ void OpenGLSWFrameBuffer::DrawLetterbox() } } -//========================================================================== -// -// OpenGLSWFrameBuffer :: DoWindowedGamma -// -// Draws the render target texture to the real back buffer using a gamma- -// correcting pixel shader. -// -//========================================================================== - -void OpenGLSWFrameBuffer::DoWindowedGamma() -{ - /*if (OldRenderTarget != nullptr) - { - FBVERTEX verts[4]; - - CalcFullscreenCoords(verts, false, true, 0, 0xFFFFFFFF); - SetRenderTarget(0, OldRenderTarget); - SetTexture(0, TempRenderTexture); - SetPixelShader(Windowed && GammaShader ? GammaShader : Shaders[SHADER_NormalColor]); - SetAlphaBlend(0); - EnableAlphaTest(false); - DrawTriangleFans(2, verts); - delete OldRenderTarget; - OldRenderTarget = nullptr; - }*/ -} - void OpenGLSWFrameBuffer::UploadPalette() { if (PaletteTexture->Buffers[0] == 0) diff --git a/src/gl/system/gl_swframebuffer.h b/src/gl/system/gl_swframebuffer.h index fca5c0839..716c77b9c 100644 --- a/src/gl/system/gl_swframebuffer.h +++ b/src/gl/system/gl_swframebuffer.h @@ -113,6 +113,16 @@ private: int Format = 0; }; + class HWFrameBuffer + { + public: + ~HWFrameBuffer(); + + int Framebuffer = 0; + HWTexture *Texture = nullptr; + }; + + class HWVertexBuffer { public: @@ -157,6 +167,7 @@ private: int BurnLocation = -1; }; + bool CreateFrameBuffer(const FString &name, int width, int height, HWFrameBuffer **outFramebuffer); bool CreatePixelShader(FString vertexsrc, FString fragmentsrc, const FString &defines, HWPixelShader **outShader); bool CreateVertexBuffer(int size, HWVertexBuffer **outVertexBuffer); bool CreateIndexBuffer(int size, HWIndexBuffer **outIndexBuffer); @@ -376,7 +387,6 @@ private: bool SetStyle(OpenGLTex *tex, DrawParms &parms, uint32_t &color0, uint32_t &color1, BufferedTris &quad); static int GetStyleAlpha(int type); static void SetColorOverlay(uint32_t color, float alpha, uint32_t &color0, uint32_t &color1); - void DoWindowedGamma(); void AddColorOnlyQuad(int left, int top, int width, int height, uint32_t color); void AddColorOnlyRect(int left, int top, int width, int height, uint32_t color); void CheckQuadBatch(int numtris = 2, int numverts = 4); @@ -404,6 +414,8 @@ private: float ShaderConstants[NumPSCONST * 4]; HWPixelShader *CurrentShader = nullptr; + HWFrameBuffer *OutputFB = nullptr; + bool AlphaTestEnabled = false; bool AlphaBlendEnabled = false; int AlphaBlendOp = 0; @@ -455,7 +467,6 @@ private: enum { BATCH_None, BATCH_Quads, BATCH_Lines } BatchType; HWPixelShader *Shaders[NUM_SHADERS]; - HWPixelShader *GammaShader = nullptr; HWTexture *InitialWipeScreen = nullptr, *FinalWipeScreen = nullptr; diff --git a/src/win32/hardware.cpp b/src/win32/hardware.cpp index 1d6e65477..095edc47e 100644 --- a/src/win32/hardware.cpp +++ b/src/win32/hardware.cpp @@ -141,7 +141,6 @@ void I_InitGraphics () if (currentrenderer == 1) Video = gl_CreateVideo(); else Video = new Win32Video(0); #else - currentrenderer = vid_renderer; Video = gl_CreateVideo(); #endif From 3c7d6234cb8e357761aaca8a358ff9ce3fa6e664 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Tue, 11 Oct 2016 14:37:57 +0200 Subject: [PATCH 7/7] Add wipes --- src/CMakeLists.txt | 1 + src/gl/system/gl_swframebuffer.cpp | 59 ++- src/gl/system/gl_swframebuffer.h | 26 +- src/gl/system/gl_swwipe.cpp | 592 +++++++++++++++++++++++++ wadsrc/static/shaders/glsl/swshader.vp | 2 + 5 files changed, 669 insertions(+), 11 deletions(-) create mode 100644 src/gl/system/gl_swwipe.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dd241a3d7..97bdfe81b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1152,6 +1152,7 @@ set( FASTMATH_SOURCES gl/system/gl_interface.cpp gl/system/gl_framebuffer.cpp gl/system/gl_swframebuffer.cpp + gl/system/gl_swwipe.cpp gl/system/gl_debug.cpp gl/system/gl_menu.cpp gl/system/gl_wipe.cpp diff --git a/src/gl/system/gl_swframebuffer.cpp b/src/gl/system/gl_swframebuffer.cpp index 4f8c25742..40d76aab1 100644 --- a/src/gl/system/gl_swframebuffer.cpp +++ b/src/gl/system/gl_swframebuffer.cpp @@ -461,6 +461,28 @@ bool OpenGLSWFrameBuffer::CreateTexture(const FString &name, int width, int heig return true; } +OpenGLSWFrameBuffer::HWTexture *OpenGLSWFrameBuffer::CopyCurrentScreen() +{ + auto obj = std::make_unique(); + obj->Format = GL_RGBA16F; + + GLint oldBinding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldBinding); + + glGenTextures(1, (GLuint*)&obj->Texture); + glBindTexture(GL_TEXTURE_2D, obj->Texture); + + glCopyTexImage2D(GL_TEXTURE_2D, 0, obj->Format, 0, 0, Width, Height, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + FGLDebug::LabelObject(GL_TEXTURE, obj->Texture, "CopyCurrentScreen"); + + glBindTexture(GL_TEXTURE_2D, oldBinding); + + return obj.release(); +} + void OpenGLSWFrameBuffer::SetGammaRamp(const GammaRamp *ramp) { } @@ -548,6 +570,39 @@ void OpenGLSWFrameBuffer::DrawTriangleFans(int count, const FBVERTEX *vertices) glBindVertexArray(oldBinding); } +void OpenGLSWFrameBuffer::DrawTriangleFans(int count, const BURNVERTEX *vertices) +{ + count = 2 + count; + + GLint oldBinding = 0; + glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &oldBinding); + + if (!StreamVertexBufferBurn) + { + StreamVertexBufferBurn = std::make_unique(); + glGenVertexArrays(1, (GLuint*)&StreamVertexBufferBurn->VertexArray); + glGenBuffers(1, (GLuint*)&StreamVertexBufferBurn->Buffer); + glBindVertexArray(StreamVertexBufferBurn->VertexArray); + glBindBuffer(GL_ARRAY_BUFFER, StreamVertexBufferBurn->Buffer); + glBufferData(GL_ARRAY_BUFFER, count * sizeof(BURNVERTEX), vertices, GL_STREAM_DRAW); + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, sizeof(BURNVERTEX), (const GLvoid*)offsetof(BURNVERTEX, x)); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(BURNVERTEX), (const GLvoid*)offsetof(BURNVERTEX, tu0)); + } + else + { + glBindVertexArray(StreamVertexBufferBurn->VertexArray); + glBindBuffer(GL_ARRAY_BUFFER, StreamVertexBufferBurn->Buffer); + glBufferData(GL_ARRAY_BUFFER, count * sizeof(BURNVERTEX), vertices, GL_STREAM_DRAW); + } + + glDrawArrays(GL_TRIANGLE_FAN, 0, count); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(oldBinding); +} + void OpenGLSWFrameBuffer::DrawPoints(int count, const FBVERTEX *vertices) { GLint oldBinding = 0; @@ -600,8 +655,8 @@ void OpenGLSWFrameBuffer::Present() FBVERTEX verts[4]; CalcFullscreenCoords(verts, false, true, 0, 0xFFFFFFFF); - for (int i = 0; i < 4; i++) - verts[i].tv = 1.0f - verts[i].tv; + //for (int i = 0; i < 4; i++) + // verts[i].tv = 1.0f - verts[i].tv; SetTexture(0, OutputFB->Texture); SetPixelShader(Shaders[SHADER_GammaCorrection]); SetAlphaBlend(0); diff --git a/src/gl/system/gl_swframebuffer.h b/src/gl/system/gl_swframebuffer.h index 716c77b9c..1cf0288ce 100644 --- a/src/gl/system/gl_swframebuffer.h +++ b/src/gl/system/gl_swframebuffer.h @@ -64,19 +64,26 @@ public: void DrawLine(int x0, int y0, int x1, int y1, int palColor, uint32 realcolor) override; void DrawPixel(int x, int y, int palcolor, uint32 rgbcolor) override; void FillSimplePoly(FTexture *tex, FVector2 *points, int npoints, double originx, double originy, double scalex, double scaley, DAngle rotation, FDynamicColormap *colormap, int lightlevel) override; - //bool WipeStartScreen(int type) override; - //void WipeEndScreen() override; - //bool WipeDo(int ticks) override; - //void WipeCleanup() override; + bool WipeStartScreen(int type) override; + void WipeEndScreen() override; + bool WipeDo(int ticks) override; + void WipeCleanup() override; bool Is8BitMode() override { return false; } int GetTrueHeight() override { return TrueHeight; } private: struct FBVERTEX { - FLOAT x, y, z, rhw; + float x, y, z, rhw; uint32_t color0, color1; - FLOAT tu, tv; + float tu, tv; + }; + + struct BURNVERTEX + { + float x, y, z, rhw; + float tu0, tv0; + float tu1, tv1; }; enum @@ -178,6 +185,7 @@ private: void SetStreamSource(HWVertexBuffer *vertexBuffer); void SetIndices(HWIndexBuffer *indexBuffer); void DrawTriangleFans(int count, const FBVERTEX *vertices); + void DrawTriangleFans(int count, const BURNVERTEX *vertices); void DrawPoints(int count, const FBVERTEX *vertices); void DrawLineList(int count); void DrawTriangleList(int minIndex, int numVertices, int startIndex, int primitiveCount); @@ -376,7 +384,7 @@ private: void UploadPalette(); void CalcFullscreenCoords(FBVERTEX verts[4], bool viewarea_only, bool can_double, uint32_t color0, uint32_t color1) const; bool Reset(); - HWTexture *GetCurrentScreen(); + HWTexture *CopyCurrentScreen(); void ReleaseDefaultPoolItems(); void KillNativePals(); void KillNativeTexs(); @@ -410,7 +418,7 @@ private: std::shared_ptr Debug; - std::unique_ptr StreamVertexBuffer; + std::unique_ptr StreamVertexBuffer, StreamVertexBufferBurn; float ShaderConstants[NumPSCONST * 4]; HWPixelShader *CurrentShader = nullptr; @@ -476,7 +484,7 @@ private: virtual ~Wiper(); virtual bool Run(int ticks, OpenGLSWFrameBuffer *fb) = 0; - //void DrawScreen(OpenGLSWFrameBuffer *fb, HWTexture *tex, int blendop = 0, uint32_t color0 = 0, uint32_t color1 = 0xFFFFFFF); + void DrawScreen(OpenGLSWFrameBuffer *fb, HWTexture *tex, int blendop = 0, uint32_t color0 = 0, uint32_t color1 = 0xFFFFFFF); }; class Wiper_Melt; friend class Wiper_Melt; diff --git a/src/gl/system/gl_swwipe.cpp b/src/gl/system/gl_swwipe.cpp new file mode 100644 index 000000000..2f36272b6 --- /dev/null +++ b/src/gl/system/gl_swwipe.cpp @@ -0,0 +1,592 @@ +/* +** gl_swwipe.cpp +** Implements the different screen wipes using OpenGL calls. +** +**--------------------------------------------------------------------------- +** Copyright 1998-2008 Randy Heit +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +// HEADER FILES ------------------------------------------------------------ + +#include "gl/system/gl_system.h" +#include "files.h" +#include "m_swap.h" +#include "v_video.h" +#include "doomstat.h" +#include "m_png.h" +#include "m_crc32.h" +#include "vectors.h" +#include "v_palette.h" +#include "templates.h" + +#include "c_dispatch.h" +#include "templates.h" +#include "i_system.h" +#include "i_video.h" +#include "i_input.h" +#include "v_pfx.h" +#include "stats.h" +#include "doomerrors.h" +#include "r_main.h" +#include "r_data/r_translate.h" +#include "f_wipe.h" +#include "sbar.h" +#include "w_wad.h" +#include "r_data/colormaps.h" + +#include "gl/system/gl_interface.h" +#include "gl/system/gl_swframebuffer.h" +#include "gl/data/gl_data.h" +#include "gl/utility/gl_clock.h" +#include "gl/utility/gl_templates.h" +#include "gl/gl_functions.h" +#include "gl_debug.h" +#include "m_random.h" + +class OpenGLSWFrameBuffer::Wiper_Crossfade : public OpenGLSWFrameBuffer::Wiper +{ +public: + Wiper_Crossfade(); + bool Run(int ticks, OpenGLSWFrameBuffer *fb); + +private: + int Clock; +}; + +class OpenGLSWFrameBuffer::Wiper_Melt : public OpenGLSWFrameBuffer::Wiper +{ +public: + Wiper_Melt(); + bool Run(int ticks, OpenGLSWFrameBuffer *fb); + +private: + // Match the strip sizes that oldschool Doom used. + static const int WIDTH = 160, HEIGHT = 200; + int y[WIDTH]; +}; + +class OpenGLSWFrameBuffer::Wiper_Burn : public OpenGLSWFrameBuffer::Wiper +{ +public: + Wiper_Burn(OpenGLSWFrameBuffer *fb); + ~Wiper_Burn(); + bool Run(int ticks, OpenGLSWFrameBuffer *fb); + +private: + static const int WIDTH = 64, HEIGHT = 64; + uint8_t BurnArray[WIDTH * (HEIGHT + 5)]; + HWTexture *BurnTexture; + int Density; + int BurnTime; +}; + +//========================================================================== +// +// OpenGLSWFrameBuffer :: WipeStartScreen +// +// Called before the current screen has started rendering. This needs to +// save what was drawn the previous frame so that it can be animated into +// what gets drawn this frame. +// +// In fullscreen mode, we use GetFrontBufferData() to grab the data that +// is visible on screen right now. +// +// In windowed mode, we can't do that because we'll get the whole desktop. +// Instead, we can conveniently use the TempRenderTexture, which is normally +// used for gamma-correcting copying the image to the back buffer. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::WipeStartScreen(int type) +{ + if (!Accel2D) + { + return Super::WipeStartScreen(type); + } + + switch (type) + { + case wipe_Melt: + ScreenWipe = new Wiper_Melt; + break; + + case wipe_Burn: + ScreenWipe = new Wiper_Burn(this); + break; + + case wipe_Fade: + ScreenWipe = new Wiper_Crossfade; + break; + + default: + return false; + } + + InitialWipeScreen = CopyCurrentScreen(); + + // Make even fullscreen model render to the TempRenderTexture, so + // we can have a copy of the new screen readily available. + GatheringWipeScreen = true; + return true; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: WipeEndScreen +// +// The screen we want to animate to has just been drawn. This function is +// called in place of Update(), so it has not been Presented yet. +// +//========================================================================== + +void OpenGLSWFrameBuffer::WipeEndScreen() +{ + if (!Accel2D) + { + Super::WipeEndScreen(); + return; + } + + // Don't do anything if there is no starting point. + if (InitialWipeScreen == NULL) + { + return; + } + + // If the whole screen was drawn without 2D accel, get it in to + // video memory now. + if (!In2D) + { + Begin2D(true); + } + + EndBatch(); // Make sure all batched primitives have been drawn. + + FinalWipeScreen = CopyCurrentScreen(); + + // At this point, InitialWipeScreen holds the screen we are wiping from. + // FinalWipeScreen holds the screen we are wiping to, which may be the + // same texture as TempRenderTexture. +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: WipeDo +// +// Perform the actual wipe animation. The number of tics since the last +// time this function was called is passed in. Returns true when the wipe +// is over. The first time this function has been called, the screen is +// still locked from before and EndScene() still has not been called. +// Successive times need to call BeginScene(). +// +//========================================================================== + +bool OpenGLSWFrameBuffer::WipeDo(int ticks) +{ + if (!Accel2D) + { + return Super::WipeDo(ticks); + } + + // Sanity checks. + if (InitialWipeScreen == NULL || FinalWipeScreen == NULL) + { + return true; + } + if (GatheringWipeScreen) + { // This is the first time we've been called for this wipe. + GatheringWipeScreen = false; + } + else + { // This is the second or later time we've been called for this wipe. + InScene = true; + } + + In2D = 3; + + EnableAlphaTest(false); + bool done = ScreenWipe->Run(ticks, this); + DrawLetterbox(); + return done; +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: WipeCleanup +// +// Release any resources that were specifically created for the wipe. +// +//========================================================================== + +void OpenGLSWFrameBuffer::WipeCleanup() +{ + if (ScreenWipe != NULL) + { + delete ScreenWipe; + ScreenWipe = NULL; + } + SafeRelease( InitialWipeScreen ); + SafeRelease( FinalWipeScreen ); + GatheringWipeScreen = false; + if (!Accel2D) + { + Super::WipeCleanup(); + return; + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper Constructor +// +//========================================================================== + +OpenGLSWFrameBuffer::Wiper::~Wiper() +{ +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper :: DrawScreen +// +// Draw either the initial or target screen completely to the screen. +// +//========================================================================== + +void OpenGLSWFrameBuffer::Wiper::DrawScreen(OpenGLSWFrameBuffer *fb, HWTexture *tex, + int blendop, uint32_t color0, uint32_t color1) +{ + FBVERTEX verts[4]; + + fb->CalcFullscreenCoords(verts, false, false, color0, color1); + fb->SetTexture(0, tex); + fb->SetAlphaBlend(blendop, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + fb->SetPixelShader(fb->Shaders[SHADER_NormalColor]); + fb->DrawTriangleFans(2, verts); +} + +// WIPE: CROSSFADE --------------------------------------------------------- + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper_Crossfade Constructor +// +//========================================================================== + +OpenGLSWFrameBuffer::Wiper_Crossfade::Wiper_Crossfade() +: Clock(0) +{ +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper_Crossfade :: Run +// +// Fades the old screen into the new one over 32 ticks. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::Wiper_Crossfade::Run(int ticks, OpenGLSWFrameBuffer *fb) +{ + Clock += ticks; + + // Put the initial screen back to the buffer. + DrawScreen(fb, fb->InitialWipeScreen); + + // Draw the new screen on top of it. + DrawScreen(fb, fb->FinalWipeScreen, GL_FUNC_ADD, ColorValue(0,0,0,Clock / 32.f), ColorRGBA(255,255,255,0)); + + return Clock >= 32; +} + +// WIPE: MELT -------------------------------------------------------------- + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper_Melt Constructor +// +//========================================================================== + +OpenGLSWFrameBuffer::Wiper_Melt::Wiper_Melt() +{ + int i, r; + + // setup initial column positions + // (y<0 => not ready to scroll yet) + y[0] = -(M_Random() & 15); + for (i = 1; i < WIDTH; ++i) + { + r = (M_Random()%3) - 1; + y[i] = clamp(y[i-1] + r, -15, 0); + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper_Melt :: Run +// +// Fades the old screen into the new one over 32 ticks. +// +//========================================================================== + +bool OpenGLSWFrameBuffer::Wiper_Melt::Run(int ticks, OpenGLSWFrameBuffer *fb) +{ + // Draw the new screen on the bottom. + DrawScreen(fb, fb->FinalWipeScreen); + + int i, dy; + int fbwidth = fb->Width; + int fbheight = fb->Height; + bool done = true; + + // Copy the old screen in vertical strips on top of the new one. + while (ticks--) + { + done = true; + for (i = 0; i < WIDTH; i++) + { + if (y[i] < 0) + { + y[i]++; + done = false; + } + else if (y[i] < HEIGHT) + { + dy = (y[i] < 16) ? y[i]+1 : 8; + y[i] = MIN(y[i] + dy, HEIGHT); + done = false; + } + if (ticks == 0) + { // Only draw for the final tick. + RECT rect; + POINT dpt; + + dpt.x = i * fbwidth / WIDTH; + dpt.y = MAX(0, y[i] * fbheight / HEIGHT); + rect.left = dpt.x; + rect.top = 0; + rect.right = (i + 1) * fbwidth / WIDTH; + rect.bottom = fbheight - dpt.y; + if (rect.bottom > rect.top) + { + fb->CheckQuadBatch(); + + BufferedTris *quad = &fb->QuadExtra[fb->QuadBatchPos]; + FBVERTEX *vert = &fb->VertexData[fb->VertexPos]; + WORD *index = &fb->IndexData[fb->IndexPos]; + + quad->ClearSetup(); + quad->Flags = BQF_DisableAlphaTest; + quad->ShaderNum = BQS_Plain; + quad->Palette = NULL; + quad->Texture = fb->InitialWipeScreen; + quad->NumVerts = 4; + quad->NumTris = 2; + + // Fill the vertex buffer. + float u0 = rect.left / float(fb->FBWidth); + float v0 = 0; + float u1 = rect.right / float(fb->FBWidth); + float v1 = (rect.bottom - rect.top) / float(fb->FBHeight); + + float x0 = float(rect.left) - 0.5f; + float x1 = float(rect.right) - 0.5f; + float y0 = float(dpt.y + fb->LBOffsetI) - 0.5f; + float y1 = float(fbheight + fb->LBOffsetI) - 0.5f; + + vert[0].x = x0; + vert[0].y = y0; + vert[0].z = 0; + vert[0].rhw = 1; + vert[0].color0 = 0; + vert[0].color1 = 0xFFFFFFF; + vert[0].tu = u0; + vert[0].tv = v0; + + vert[1].x = x1; + vert[1].y = y0; + vert[1].z = 0; + vert[1].rhw = 1; + vert[1].color0 = 0; + vert[1].color1 = 0xFFFFFFF; + vert[1].tu = u1; + vert[1].tv = v0; + + vert[2].x = x1; + vert[2].y = y1; + vert[2].z = 0; + vert[2].rhw = 1; + vert[2].color0 = 0; + vert[2].color1 = 0xFFFFFFF; + vert[2].tu = u1; + vert[2].tv = v1; + + vert[3].x = x0; + vert[3].y = y1; + vert[3].z = 0; + vert[3].rhw = 1; + vert[3].color0 = 0; + vert[3].color1 = 0xFFFFFFF; + vert[3].tu = u0; + vert[3].tv = v1; + + // Fill the vertex index buffer. + index[0] = fb->VertexPos; + index[1] = fb->VertexPos + 1; + index[2] = fb->VertexPos + 2; + index[3] = fb->VertexPos; + index[4] = fb->VertexPos + 2; + index[5] = fb->VertexPos + 3; + + // Batch the quad. + fb->QuadBatchPos++; + fb->VertexPos += 4; + fb->IndexPos += 6; + } + } + } + } + fb->EndQuadBatch(); + return done; +} + +// WIPE: BURN -------------------------------------------------------------- + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper_Burn Constructor +// +//========================================================================== + +OpenGLSWFrameBuffer::Wiper_Burn::Wiper_Burn(OpenGLSWFrameBuffer *fb) +{ + Density = 4; + BurnTime = 0; + memset(BurnArray, 0, sizeof(BurnArray)); + if (fb->Shaders[SHADER_BurnWipe] == NULL || !fb->CreateTexture("BurnWipe", WIDTH, HEIGHT, 1, GL_R8, &BurnTexture)) + { + BurnTexture = NULL; + } +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper_Burn Destructor +// +//========================================================================== + +OpenGLSWFrameBuffer::Wiper_Burn::~Wiper_Burn() +{ + SafeRelease( BurnTexture ); +} + +//========================================================================== +// +// OpenGLSWFrameBuffer :: Wiper_Burn :: Run +// +//========================================================================== + +bool OpenGLSWFrameBuffer::Wiper_Burn::Run(int ticks, OpenGLSWFrameBuffer *fb) +{ + bool done; + + BurnTime += ticks; + ticks *= 2; + + // Make the fire burn + done = false; + while (!done && ticks--) + { + Density = wipe_CalcBurn(BurnArray, WIDTH, HEIGHT, Density); + done = (Density < 0); + } + + // Update the burn texture with the new burn data + + if (BurnTexture->Buffers[0] == 0) + { + glGenBuffers(2, (GLuint*)BurnTexture->Buffers); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, BurnTexture->Buffers[0]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, WIDTH * HEIGHT, nullptr, GL_STREAM_DRAW); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, BurnTexture->Buffers[1]); + glBufferData(GL_PIXEL_UNPACK_BUFFER, WIDTH * HEIGHT, nullptr, GL_STREAM_DRAW); + } + else + { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, BurnTexture->Buffers[BurnTexture->CurrentBuffer]); + BurnTexture->CurrentBuffer = (BurnTexture->CurrentBuffer + 1) & 1; + } + + uint8_t *dest = (uint8_t*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, WIDTH * HEIGHT, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT); + if (dest) + { + memcpy(dest, BurnArray, WIDTH * HEIGHT); + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + + GLint oldBinding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &oldBinding); + glBindTexture(GL_TEXTURE_2D, BurnTexture->Texture); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, WIDTH, HEIGHT, GL_RED, GL_UNSIGNED_BYTE, 0); + glBindTexture(GL_TEXTURE_2D, oldBinding); + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + } + + // Put the initial screen back to the buffer. + DrawScreen(fb, fb->InitialWipeScreen); + + // Burn the new screen on top of it. + float top = fb->LBOffset - 0.5f; + float right = float(fb->Width) - 0.5f; + float bot = float(fb->Height) + top; + float texright = float(fb->Width) / float(fb->FBWidth); + float texbot = float(fb->Height) / float(fb->FBHeight); + + BURNVERTEX verts[4] = + { + { -0.5f, top, 0.5f, 1.f, 0.f, 0.f, 0, 0 }, + { right, top, 0.5f, 1.f, texright, 0.f, 1, 0 }, + { right, bot, 0.5f, 1.f, texright, texbot, 1, 1 }, + { -0.5f, bot, 0.5f, 1.f, 0.f, texbot, 0, 1 } + }; + + fb->SetTexture(0, fb->FinalWipeScreen); + fb->SetTexture(1, BurnTexture); + fb->SetAlphaBlend(GL_FUNC_ADD, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + fb->SetPixelShader(fb->Shaders[SHADER_BurnWipe]); + glActiveTexture(GL_TEXTURE1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + fb->DrawTriangleFans(2, verts); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glActiveTexture(GL_TEXTURE0); + + // The fire may not always stabilize, so the wipe is forced to end + // after an arbitrary maximum time. + return done || (BurnTime > 40); +} diff --git a/wadsrc/static/shaders/glsl/swshader.vp b/wadsrc/static/shaders/glsl/swshader.vp index a95be1e1a..a317025aa 100644 --- a/wadsrc/static/shaders/glsl/swshader.vp +++ b/wadsrc/static/shaders/glsl/swshader.vp @@ -13,7 +13,9 @@ uniform vec4 ScreenSize; void main() { gl_Position = vec4(AttrPosition.xy / ScreenSize.xy * 2.0 - 1.0, 1.0, 1.0); +#if defined(EGAMMACORRECTION) gl_Position.y = -gl_Position.y; +#endif PixelColor0 = AttrColor0.bgra; PixelColor1 = AttrColor1.bgra; PixelTexCoord0 = AttrTexCoord0;