blob: c72f4baec60b7dce7cf7a5cce9284059642c0c7b [file] [log] [blame]
// SwiftShader Software Renderer
//
// Copyright(c) 2005-2012 TransGaming Inc.
//
// All rights reserved. No part of this software may be copied, distributed, transmitted,
// transcribed, stored in a retrieval system, translated into any human or computer
// language by any means, or disclosed to third parties without the explicit written
// agreement of TransGaming Inc. Without such an agreement, no rights or licenses, express
// or implied, including but not limited to any patent rights, are granted to you.
//
#include "FrameBuffer.hpp"
#include "Timer.hpp"
#include "CPUID.hpp"
#include "serialvalid.h"
#include "Surface.hpp"
#include "Register.hpp"
#include "Debug.hpp"
#include "Reactor/Reactor.hpp"
#include "Common/Configurator.hpp"
#include <stdio.h>
extern const int logoWidth;
extern const int logoHeight;
extern const unsigned int logoData[];
#ifndef DISPLAY_LOGO
#define DISPLAY_LOGO (NDEBUG & 1)
#endif
#define ASYNCHRONOUS_BLIT 0 // FIXME: Currently leads to rare race conditions
namespace sw
{
extern bool forceWindowed;
Surface *FrameBuffer::logo;
unsigned int *FrameBuffer::logoImage;
void *FrameBuffer::cursor;
int FrameBuffer::cursorWidth = 0;
int FrameBuffer::cursorHeight = 0;
int FrameBuffer::cursorHotspotX;
int FrameBuffer::cursorHotspotY;
int FrameBuffer::cursorPositionX;
int FrameBuffer::cursorPositionY;
int FrameBuffer::cursorX;
int FrameBuffer::cursorY;
bool FrameBuffer::topLeftOrigin = false;
FrameBuffer::FrameBuffer(HWND windowHandle, int width, int height, bool fullscreen, bool topLeftOrigin)
{
this->topLeftOrigin = topLeftOrigin;
locked = 0;
this->windowHandle = windowHandle;
this->width = width;
this->height = height;
bitDepth = 32;
HDRdisplay = false;
stride = 0;
if(forceWindowed)
{
fullscreen = false;
}
windowed = !fullscreen;
if(!windowed)
{
// Force fullscreen window style (no borders)
originalWindowStyle = GetWindowLong(windowHandle, GWL_STYLE);
SetWindowLong(windowHandle, GWL_STYLE, WS_POPUP);
}
HINSTANCE instance = (HINSTANCE)GetWindowLongPtr(windowHandle, GWLP_HINSTANCE);
unsigned char one[32 * 32 / sizeof(unsigned char)];
memset(one, 0xFFFFFFFF, sizeof(one));
unsigned char zero[32 * 32 / sizeof(unsigned char)] = {0};
nullCursor = CreateCursor(instance, 0, 0, 32, 32, one, zero);
win32Cursor = GetCursor();
blitFunction = 0;
blitRoutine = 0;
blitState.width = 0;
blitState.height = 0;
blitState.depth = 0;
blitState.cursorWidth = 0;
blitState.cursorHeight = 0;
logo = 0;
if(ASYNCHRONOUS_BLIT)
{
syncEvent = CreateEvent(0, FALSE, FALSE, 0);
blitEvent = CreateEvent(0, FALSE, FALSE, 0);
FrameBuffer *parameters = this;
terminate = false;
blitThread = CreateThread(0, 1024 * 1024, threadFunction, &parameters, 0, 0);
WaitForSingleObject(syncEvent, INFINITE);
}
}
FrameBuffer::~FrameBuffer()
{
if(ASYNCHRONOUS_BLIT)
{
terminate = true;
SetEvent(blitEvent);
WaitForSingleObject(blitThread, INFINITE);
CloseHandle(blitThread);
CloseHandle(blitEvent);
CloseHandle(syncEvent);
}
delete blitRoutine;
DestroyCursor(nullCursor);
if(!windowed && GetWindowLong(windowHandle, GWL_STYLE) == WS_POPUP)
{
SetWindowLong(windowHandle, GWL_STYLE, originalWindowStyle);
}
}
void FrameBuffer::updateBounds(HWND windowOverride)
{
HWND window = windowOverride ? windowOverride : windowHandle;
if(windowed)
{
GetClientRect(window, &bounds);
ClientToScreen(window, (POINT*)&bounds);
ClientToScreen(window, (POINT*)&bounds + 1);
}
else
{
SetRect(&bounds, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
}
}
int FrameBuffer::getWidth() const
{
return width;
}
int FrameBuffer::getHeight() const
{
return height;
}
int FrameBuffer::getStride() const
{
return stride;
}
void FrameBuffer::setCursorImage(sw::Surface *cursorImage)
{
if(cursorImage)
{
cursor = cursorImage->lockExternal(0, 0, 0, sw::LOCK_READONLY, sw::PUBLIC);
cursorImage->unlockExternal();
cursorWidth = cursorImage->getExternalWidth();
cursorHeight = cursorImage->getExternalHeight();
}
else
{
cursorWidth = 0;
cursorHeight = 0;
}
}
void FrameBuffer::setCursorOrigin(int x0, int y0)
{
cursorHotspotX = x0;
cursorHotspotY = y0;
}
void FrameBuffer::setCursorPosition(int x, int y)
{
cursorPositionX = x;
cursorPositionY = y;
}
void FrameBuffer::copy(HWND windowOverride, void *source, bool HDR)
{
if(!source)
{
return;
}
updateBounds(windowOverride);
if(!lock())
{
return;
}
if(topLeftOrigin)
{
target = source;
}
else
{
const int width2 = (width + 1) & ~1;
const int sBytes = HDR ? 8 : 4;
const int sStride = sBytes * width2;
target = (byte*)source + (height - 1) * sStride;
}
HDRdisplay = HDR;
if(cursor)
{
cursorX = cursorPositionX - cursorHotspotX;
cursorY = cursorPositionY - cursorHotspotY;
HCURSOR oldCursor = SetCursor(nullCursor);
if(oldCursor != nullCursor)
{
win32Cursor = oldCursor;
}
}
else
{
if(GetCursor() == nullCursor)
{
SetCursor(win32Cursor);
}
}
if(ASYNCHRONOUS_BLIT)
{
SetEvent(blitEvent);
WaitForSingleObject(syncEvent, INFINITE);
}
else
{
copyLocked();
}
unlock();
}
void FrameBuffer::copyLocked()
{
BlitState update;
update.width = width;
update.height = height;
update.depth = bitDepth;
update.stride = stride;
update.HDR = HDRdisplay;
update.cursorWidth = cursorWidth;
update.cursorHeight = cursorHeight;
if(memcmp(&blitState, &update, sizeof(BlitState)) != 0)
{
blitState = update;
delete blitRoutine;
blitRoutine = copyRoutine(blitState);
blitFunction = (void(__cdecl*)(void*, void*))blitRoutine->getEntry();
}
blitFunction(locked, target);
#ifndef _M_AMD64
__asm emms
#endif
}
Routine *FrameBuffer::copyRoutine(const BlitState &state)
{
initializeLogo();
const int width = state.width;
const int height = state.height;
const int width2 = (state.width + 1) & ~1;
const int dBytes = state.depth / 8;
const int dStride = state.stride;
const int sBytes = state.HDR ? 8 : 4;
const int sStride = topLeftOrigin ? (sBytes * width2) : -(sBytes * width2);
// char compareApp[32] = SCRAMBLE31(validationApp, APPNAME_SCRAMBLE);
// bool validApp = strcmp(compareApp, registeredApp) == 0;
bool validKey = ValidateSerialNumber(validationKey, CHECKSUM_KEY, SERIAL_PREFIX);
Function<Void, Pointer<Byte>, Pointer<Byte>> function;
{
Pointer<Byte> dst(function.arg(0));
Pointer<Byte> src(function.arg(1));
For(Int y = 0, y < height, y++)
{
Pointer<Byte> d = dst + y * dStride;
Pointer<Byte> s = src + y * sStride;
Int x0 = 0;
#if DISPLAY_LOGO
If(!Bool(validKey)/* || !Bool(validApp)*/)
{
If(y > height - logoHeight)
{
x0 = logoWidth;
s += logoWidth * sBytes;
d += logoWidth * dBytes;
}
}
#endif
if(state.depth == 32)
{
if(width2 % 4 == 0 && !state.HDR)
{
For(Int x = x0, x < width2, x += 4)
{
*Pointer<Int4>(d, 1) = *Pointer<Int4>(s, 16);
s += 4 * sBytes;
d += 4 * dBytes;
}
}
else
{
For(Int x = x0, x < width2, x += 2)
{
Int2 c01;
if(!state.HDR)
{
c01 = *Pointer<Int2>(s);
}
else
{
UShort4 c0 = As<UShort4>(Swizzle(*Pointer<Short4>(s + 0), 0xC6)) >> 8;
UShort4 c1 = As<UShort4>(Swizzle(*Pointer<Short4>(s + 8), 0xC6)) >> 8;
c01 = As<Int2>(Pack(c0, c1));
}
*Pointer<Int2>(d) = c01;
s += 2 * sBytes;
d += 2 * dBytes;
}
}
}
else if(state.depth == 24)
{
For(Int x = x0, x < width, x++)
{
if(!state.HDR)
{
*Pointer<Byte>(d + 0) = *Pointer<Byte>(s + 0);
*Pointer<Byte>(d + 1) = *Pointer<Byte>(s + 1);
*Pointer<Byte>(d + 2) = *Pointer<Byte>(s + 2);
}
else
{
*Pointer<Byte>(d + 0) = *Pointer<Byte>(s + 5);
*Pointer<Byte>(d + 1) = *Pointer<Byte>(s + 3);
*Pointer<Byte>(d + 2) = *Pointer<Byte>(s + 1);
}
s += sBytes;
d += dBytes;
}
}
else if(state.depth == 16)
{
For(Int x = x0, x < width, x++)
{
Int c;
if(!state.HDR)
{
c = *Pointer<Int>(s);
}
else
{
UShort4 cc = As<UShort4>(Swizzle(*Pointer<Short4>(s + 0), 0xC6)) >> 8;
c = Int(As<Int2>(Pack(cc, cc)));
}
*Pointer<Short>(d) = Short((c & 0x00F80000) >> 8 |
(c & 0x0000FC00) >> 5 |
(c & 0x000000F8) >> 3);
s += sBytes;
d += dBytes;
}
}
else ASSERT(false);
}
#if DISPLAY_LOGO
If(!Bool(validKey)/* || !Bool(validApp)*/)
{
UInt hash = UInt(0x0B020C04) + UInt(0xC0F090E0); // Initial value
UInt imageHash = S3TC_SUPPORT ? UInt(0x01010600) + UInt(0xC0302010) : UInt(0x0D030E0B) + UInt(0xB0D01040);
While(hash != imageHash)
{
For(y = (height - 1), height - 1 - y < logoHeight, y--)
{
Pointer<Byte> logo = *Pointer<Pointer<Byte>>(&logoImage) + 4 * (logoHeight - height + y) * logoWidth;
Pointer<Byte> s = src + y * sStride;
Pointer<Byte> d = dst + y * dStride;
For(Int x = 0, x < logoWidth, x++)
{
hash *= 16777619;
hash ^= *Pointer<UInt>(logo);
If(y >= 0 && x < width)
{
blend(state, d, s, logo);
}
logo += 4;
s += sBytes;
d += dBytes;
}
}
}
}
#endif
Int x0 = *Pointer<Int>(&cursorX);
Int y0 = *Pointer<Int>(&cursorY);
For(Int y1 = 0, y1 < cursorHeight, y1++)
{
Int y = y0 + y1;
If(y >= 0 && y < height)
{
Pointer<Byte> d = dst + y * dStride + x0 * dBytes;
Pointer<Byte> s = src + y * sStride + x0 * sBytes;
Pointer<Byte> c = *Pointer<Pointer<Byte>>(&cursor) + y1 * cursorWidth * 4;
For(Int x1 = 0, x1 < cursorWidth, x1++)
{
Int x = x0 + x1;
If(x >= 0 && x < width)
{
blend(state, d, s, c);
}
c += 4;
s += sBytes;
d += dBytes;
}
}
}
Return();
}
return function(L"FrameBuffer");
}
void FrameBuffer::blend(const BlitState &state, const Pointer<Byte> &d, const Pointer<Byte> &s, const Pointer<Byte> &c)
{
Short4 c1;
Short4 c2;
c1 = UnpackLow(As<Byte8>(c1), *Pointer<Byte8>(c));
if(!state.HDR)
{
c2 = UnpackLow(As<Byte8>(c2), *Pointer<Byte8>(s));
}
else
{
c2 = Swizzle(*Pointer<Short4>(s + 0), 0xC6);
}
c1 = As<Short4>(As<UShort4>(c1) >> 9);
c2 = As<Short4>(As<UShort4>(c2) >> 9);
Short4 alpha = Swizzle(c1, 0xFF) & Short4(0xFFFFu, 0xFFFFu, 0xFFFFu, 0x0000);
c1 = (c1 - c2) * alpha;
c1 = c1 >> 7;
c1 = c1 + c2;
c1 = c1 + c1;
c1 = As<Short4>(Pack(As<UShort4>(c1), As<UShort4>(c1)));
if(state.depth == 32)
{
*Pointer<UInt>(d) = UInt(As<Long>(c1));
}
else if(state.depth == 24)
{
Int c = Int(As<Int2>(c1));
*Pointer<Byte>(d + 0) = Byte(c >> 0);
*Pointer<Byte>(d + 1) = Byte(c >> 8);
*Pointer<Byte>(d + 2) = Byte(c >> 16);
}
else if(state.depth == 16)
{
Int c = Int(As<Int2>(c1));
*Pointer<Short>(d) = Short((c & 0x00F80000) >> 8 |
(c & 0x0000FC00) >> 5 |
(c & 0x000000F8) >> 3);
}
else ASSERT(false);
}
unsigned long __stdcall FrameBuffer::threadFunction(void *parameters)
{
FrameBuffer *frameBuffer = *static_cast<FrameBuffer**>(parameters);
SetEvent(frameBuffer->syncEvent); // Received parameters
while(!frameBuffer->terminate)
{
WaitForSingleObject(frameBuffer->blitEvent, INFINITE);
if(!frameBuffer->terminate)
{
frameBuffer->copyLocked();
SetEvent(frameBuffer->syncEvent);
}
}
return 0;
}
void FrameBuffer::initializeLogo()
{
#if DISPLAY_LOGO
if(!logo)
{
#if S3TC_SUPPORT
logo = new Surface(0, logoWidth, logoHeight, 1, FORMAT_DXT5, true, false);
void *data = logo->lockExternal(0, 0, 0, LOCK_WRITEONLY, sw::PUBLIC);
memcpy(data, logoData, logoWidth * logoHeight);
logo->unlockExternal();
#else
logo = new Surface(0, logoWidth, logoHeight, 1, FORMAT_A8R8G8B8, true, false);
void *data = logo->lockExternal(0, 0, 0, LOCK_WRITEONLY, sw::PUBLIC);
memcpy(data, logoData, logoWidth * logoHeight * 4);
logo->unlockExternal();
#endif
logoImage = (unsigned int*)logo->lockInternal(0, 0, 0, LOCK_READONLY, sw::PUBLIC);
logo->unlockInternal();
}
#endif
}
}
#include "FrameBufferDD.hpp"
#include "FrameBufferGDI.hpp"
extern "C"
{
sw::FrameBuffer *createFrameBuffer(HWND windowHandle, int width, int height, bool fullscreen, bool topLeftOrigin)
{
sw::Configurator ini("SwiftShader.ini");
int api = ini.getInteger("Testing", "FrameBufferAPI", 1);
if(api == 0)
{
return new sw::FrameBufferDD(windowHandle, width, height, fullscreen, topLeftOrigin);
}
else if(api == 1)
{
return new sw::FrameBufferGDI(windowHandle, width, height, fullscreen, topLeftOrigin);
}
else ASSERT(false);
return 0;
}
}