blob: 68e293f641c4c1b7409f8d9ad612e36172d03b82 [file] [log] [blame]
// SwiftShader Software Renderer
//
// Copyright(c) 2005-2011 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 "Device.hpp"
#include "Image.hpp"
#include "Texture.h"
#include "Renderer/Renderer.hpp"
#include "Renderer/Clipper.hpp"
#include "Shader/PixelShader.hpp"
#include "Shader/VertexShader.hpp"
#include "Main/Config.hpp"
#include "Main/FrameBuffer.hpp"
#include "Common/Math.hpp"
#include "Common/Configurator.hpp"
#include "Common/Timer.hpp"
#include "../common/debug.h"
bool localShaderConstants = true;
namespace gl
{
using namespace sw;
Device::Device(Context *context) : Renderer(context), context(context)
{
depthStencil = 0;
renderTarget = 0;
setDepthBufferEnable(true);
setFillMode(Context::FILL_SOLID);
setShadingMode(Context::SHADING_GOURAUD);
setDepthWriteEnable(true);
setAlphaTestEnable(false);
setSourceBlendFactor(Context::BLEND_ONE);
setDestBlendFactor(Context::BLEND_ZERO);
setCullMode(Context::CULL_COUNTERCLOCKWISE);
setDepthCompare(Context::DEPTH_LESSEQUAL);
setAlphaReference(0);
setAlphaCompare(Context::ALPHA_ALWAYS);
setAlphaBlendEnable(false);
setFogEnable(false);
setSpecularEnable(false);
setFogColor(0);
setPixelFogMode(Context::FOG_NONE);
setFogStart(0.0f);
setFogEnd(1.0f);
setFogDensity(1.0f);
setRangeFogEnable(false);
setStencilEnable(false);
setStencilFailOperation(Context::OPERATION_KEEP);
setStencilZFailOperation(Context::OPERATION_KEEP);
setStencilPassOperation(Context::OPERATION_KEEP);
setStencilCompare(Context::STENCIL_ALWAYS);
setStencilReference(0);
setStencilMask(0xFFFFFFFF);
setStencilWriteMask(0xFFFFFFFF);
setVertexFogMode(Context::FOG_NONE);
setClipFlags(0);
setPointSize(1.0f);
setPointSizeMin(1.0f);
setPointSpriteEnable(false);
setPointSizeMax(64.0f);
setColorWriteMask(0, 0x0000000F);
setBlendOperation(Context::BLENDOP_ADD);
scissorEnable = false;
setSlopeDepthBias(0.0f);
setTwoSidedStencil(false);
setStencilFailOperationCCW(Context::OPERATION_KEEP);
setStencilZFailOperationCCW(Context::OPERATION_KEEP);
setStencilPassOperationCCW(Context::OPERATION_KEEP);
setStencilCompareCCW(Context::STENCIL_ALWAYS);
setColorWriteMask(1, 0x0000000F);
setColorWriteMask(2, 0x0000000F);
setColorWriteMask(3, 0x0000000F);
setBlendConstant(0xFFFFFFFF);
setWriteSRGB(false);
setDepthBias(0.0f);
setSeparateAlphaBlendEnable(false);
setSourceBlendFactorAlpha(Context::BLEND_ONE);
setDestBlendFactorAlpha(Context::BLEND_ZERO);
setBlendOperationAlpha(Context::BLENDOP_ADD);
for(int i = 0; i < 16; i++)
{
setAddressingModeU(sw::SAMPLER_PIXEL, i, ADDRESSING_WRAP);
setAddressingModeV(sw::SAMPLER_PIXEL, i, ADDRESSING_WRAP);
setAddressingModeW(sw::SAMPLER_PIXEL, i, ADDRESSING_WRAP);
setBorderColor(sw::SAMPLER_PIXEL, i, 0x00000000);
setTextureFilter(sw::SAMPLER_PIXEL, i, FILTER_POINT);
setMipmapFilter(sw::SAMPLER_PIXEL, i, MIPMAP_NONE);
setMipmapLOD(sw::SAMPLER_PIXEL, i, 0.0f);
}
for(int i = 0; i < 4; i++)
{
setAddressingModeU(sw::SAMPLER_VERTEX, i, ADDRESSING_WRAP);
setAddressingModeV(sw::SAMPLER_VERTEX, i, ADDRESSING_WRAP);
setAddressingModeW(sw::SAMPLER_VERTEX, i, ADDRESSING_WRAP);
setBorderColor(sw::SAMPLER_VERTEX, i, 0x00000000);
setTextureFilter(sw::SAMPLER_VERTEX, i, FILTER_POINT);
setMipmapFilter(sw::SAMPLER_VERTEX, i, MIPMAP_NONE);
setMipmapLOD(sw::SAMPLER_VERTEX, i, 0.0f);
}
for(int i = 0; i < 6; i++)
{
float plane[4] = {0, 0, 0, 0};
setClipPlane(i, plane);
}
pixelShader = 0;
vertexShader = 0;
pixelShaderDirty = true;
pixelShaderConstantsFDirty = 0;
vertexShaderDirty = true;
vertexShaderConstantsFDirty = 0;
for(int i = 0; i < 224; i++)
{
float zero[4] = {0, 0, 0, 0};
setPixelShaderConstantF(i, zero, 1);
}
for(int i = 0; i < 256; i++)
{
float zero[4] = {0, 0, 0, 0};
setVertexShaderConstantF(i, zero, 1);
}
}
Device::~Device()
{
if(depthStencil)
{
depthStencil->unbind();
depthStencil = 0;
}
if(renderTarget)
{
renderTarget->unbind();
renderTarget = 0;
}
delete context;
}
void Device::clearColor(unsigned int color, unsigned int rgbaMask)
{
TRACE("unsigned long color = 0x%0.8X", color);
int x = viewport.x;
int y = viewport.y;
int width = viewport.width;
int height = viewport.height;
// Clamp against scissor rectangle
if(scissorEnable)
{
if(x < scissorRect.left) x = scissorRect.left;
if(y < scissorRect.top) y = scissorRect.top;
if(width > scissorRect.right - scissorRect.left) width = scissorRect.right - scissorRect.left;
if(height > scissorRect.bottom - scissorRect.top) height = scissorRect.bottom - scissorRect.top;
}
if(renderTarget)
{
renderTarget->clearColorBuffer(color, rgbaMask, x, y, width, height);
}
}
void Device::clearDepth(float z)
{
TRACE("float z = %f", z);
if(z > 1) z = 1;
if(z < 0) z = 0;
int x = viewport.x;
int y = viewport.y;
int width = viewport.width;
int height = viewport.height;
// Clamp against scissor rectangle
if(scissorEnable)
{
if(x < scissorRect.left) x = scissorRect.left;
if(y < scissorRect.top) y = scissorRect.top;
if(width > scissorRect.right - scissorRect.left) width = scissorRect.right - scissorRect.left;
if(height > scissorRect.bottom - scissorRect.top) height = scissorRect.bottom - scissorRect.top;
}
if(depthStencil)
{
depthStencil->clearDepthBuffer(z, x, y, width, height);
}
}
void Device::clearStencil(unsigned int stencil, unsigned int mask)
{
TRACE("unsigned long stencil = %d", stencil);
int x = viewport.x;
int y = viewport.y;
int width = viewport.width;
int height = viewport.height;
// Clamp against scissor rectangle
if(scissorEnable)
{
if(x < scissorRect.left) x = scissorRect.left;
if(y < scissorRect.top) y = scissorRect.top;
if(width > scissorRect.right - scissorRect.left) width = scissorRect.right - scissorRect.left;
if(height > scissorRect.bottom - scissorRect.top) height = scissorRect.bottom - scissorRect.top;
}
if(depthStencil)
{
depthStencil->clearStencilBuffer(stencil, mask, x, y, width, height);
}
}
Image *Device::createDepthStencilSurface(unsigned int width, unsigned int height, sw::Format format, int multiSampleDepth, bool discard)
{
TRACE("unsigned int width = %d, unsigned int height = %d, sw::Format format = %d, int multiSampleDepth = %d, bool discard = %d", width, height, format, multiSampleDepth, discard);
if(width == 0 || height == 0 || height > OUTLINE_RESOLUTION)
{
ERR("Invalid parameters");
return 0;
}
bool lockable = true;
switch(format)
{
// case FORMAT_D15S1:
case FORMAT_D24S8:
case FORMAT_D24X8:
// case FORMAT_D24X4S4:
case FORMAT_D24FS8:
case FORMAT_D32:
case FORMAT_D16:
lockable = false;
break;
// case FORMAT_S8_LOCKABLE:
// case FORMAT_D16_LOCKABLE:
case FORMAT_D32F_LOCKABLE:
// case FORMAT_D32_LOCKABLE:
case FORMAT_DF24:
case FORMAT_DF16:
lockable = true;
break;
default:
UNREACHABLE();
}
Image *surface = new Image(0, width, height, format, GL_NONE, GL_NONE, multiSampleDepth, lockable, true);
if(!surface)
{
ERR("Out of memory");
return 0;
}
surface->addRef();
return surface;
}
Image *Device::createOffscreenPlainSurface(unsigned int width, unsigned int height, sw::Format format)
{
TRACE("unsigned int width = %d, unsigned int height = %d, sw::Format format = %d", width, height, format);
Image *surface = new Image(0, width, height, format, GL_NONE, GL_NONE, 1, true, false);
if(!surface)
{
ERR("Out of memory");
return 0;
}
surface->addRef();
return surface;
}
Image *Device::createRenderTarget(unsigned int width, unsigned int height, sw::Format format, int multiSampleDepth, bool lockable)
{
TRACE("unsigned int width = %d, unsigned int height = %d, sw::Format format = %d, int multiSampleDepth = %d, bool lockable = %d", width, height, format, multiSampleDepth, lockable);
if(height > OUTLINE_RESOLUTION)
{
ERR("Invalid parameters");
return 0;
}
Image *surface = new Image(0, width, height, format, GL_NONE, GL_NONE, multiSampleDepth, lockable != FALSE, true);
if(!surface)
{
ERR("Out of memory");
return 0;
}
surface->addRef();
return surface;
}
void Device::drawIndexedPrimitive(PrimitiveType type, unsigned int indexOffset, unsigned int primitiveCount, int indexSize)
{
TRACE("");
if(!bindResources() || !primitiveCount)
{
return;
}
Context::DrawType drawType;
if(indexSize == 4)
{
switch(type)
{
case DRAW_POINTLIST: drawType = Context::DRAW_INDEXEDPOINTLIST32; break;
case DRAW_LINELIST: drawType = Context::DRAW_INDEXEDLINELIST32; break;
case DRAW_LINESTRIP: drawType = Context::DRAW_INDEXEDLINESTRIP32; break;
case DRAW_LINELOOP: drawType = Context::DRAW_INDEXEDLINELOOP32; break;
case DRAW_TRIANGLELIST: drawType = Context::DRAW_INDEXEDTRIANGLELIST32; break;
case DRAW_TRIANGLESTRIP: drawType = Context::DRAW_INDEXEDTRIANGLESTRIP32; break;
case DRAW_TRIANGLEFAN: drawType = Context::DRAW_INDEXEDTRIANGLEFAN32; break;
default: UNREACHABLE();
}
}
else if(indexSize == 2)
{
switch(type)
{
case DRAW_POINTLIST: drawType = Context::DRAW_INDEXEDPOINTLIST16; break;
case DRAW_LINELIST: drawType = Context::DRAW_INDEXEDLINELIST16; break;
case DRAW_LINESTRIP: drawType = Context::DRAW_INDEXEDLINESTRIP16; break;
case DRAW_LINELOOP: drawType = Context::DRAW_INDEXEDLINELOOP16; break;
case DRAW_TRIANGLELIST: drawType = Context::DRAW_INDEXEDTRIANGLELIST16; break;
case DRAW_TRIANGLESTRIP: drawType = Context::DRAW_INDEXEDTRIANGLESTRIP16; break;
case DRAW_TRIANGLEFAN: drawType = Context::DRAW_INDEXEDTRIANGLEFAN16; break;
default: UNREACHABLE();
}
}
else if(indexSize == 1)
{
switch(type)
{
case DRAW_POINTLIST: drawType = Context::DRAW_INDEXEDPOINTLIST8; break;
case DRAW_LINELIST: drawType = Context::DRAW_INDEXEDLINELIST8; break;
case DRAW_LINESTRIP: drawType = Context::DRAW_INDEXEDLINESTRIP8; break;
case DRAW_LINELOOP: drawType = Context::DRAW_INDEXEDLINELOOP8; break;
case DRAW_TRIANGLELIST: drawType = Context::DRAW_INDEXEDTRIANGLELIST8; break;
case DRAW_TRIANGLESTRIP: drawType = Context::DRAW_INDEXEDTRIANGLESTRIP8; break;
case DRAW_TRIANGLEFAN: drawType = Context::DRAW_INDEXEDTRIANGLEFAN8; break;
default: UNREACHABLE();
}
}
else UNREACHABLE();
draw(drawType, indexOffset, primitiveCount);
}
void Device::drawPrimitive(PrimitiveType primitiveType, unsigned int primitiveCount)
{
TRACE("");
if(!bindResources() || !primitiveCount)
{
return;
}
setIndexBuffer(0);
Context::DrawType drawType;
switch(primitiveType)
{
case DRAW_POINTLIST: drawType = Context::DRAW_POINTLIST; break;
case DRAW_LINELIST: drawType = Context::DRAW_LINELIST; break;
case DRAW_LINESTRIP: drawType = Context::DRAW_LINESTRIP; break;
case DRAW_LINELOOP: drawType = Context::DRAW_LINELOOP; break;
case DRAW_TRIANGLELIST: drawType = Context::DRAW_TRIANGLELIST; break;
case DRAW_TRIANGLESTRIP: drawType = Context::DRAW_TRIANGLESTRIP; break;
case DRAW_TRIANGLEFAN: drawType = Context::DRAW_TRIANGLEFAN; break;
default: UNREACHABLE();
}
draw(drawType, 0, primitiveCount);
}
Image *Device::getDepthStencilSurface()
{
TRACE("void");
if(depthStencil)
{
depthStencil->addRef();
}
return depthStencil;
}
bool Device::getRenderTargetData(Image *renderTarget, Image *destSurface)
{
TRACE("Image *renderTarget = 0x%0.8p, Image *destSurface = 0x%0.8p", renderTarget, destSurface);
if(!renderTarget || !destSurface)
{
ERR("Invalid parameters");
return false;
}
if(renderTarget->getWidth() != destSurface->getWidth() ||
renderTarget->getHeight() != destSurface->getHeight() ||
renderTarget->getInternalFormat() != destSurface->getInternalFormat())
{
ERR("Invalid parameters");
return false;
}
static void (__cdecl *blitFunction)(void *dst, void *src);
static Routine *blitRoutine;
static BlitState blitState = {0};
BlitState update;
update.width = renderTarget->getInternalWidth();
update.height = renderTarget->getInternalHeight();
update.depth = 32;
update.stride = destSurface->getInternalPitchB();
update.HDR = false;
update.cursorHeight = 0;
update.cursorWidth = 0;
if(memcmp(&blitState, &update, sizeof(BlitState)) != 0)
{
blitState = update;
delete blitRoutine;
blitRoutine = FrameBuffer::copyRoutine(blitState);
blitFunction = (void(__cdecl*)(void*, void*))blitRoutine->getEntry();
}
void *dst = destSurface->lockInternal(0, 0, 0, LOCK_WRITEONLY, PUBLIC);
void *src = renderTarget->lockInternal(0, 0, 0, LOCK_WRITEONLY, PUBLIC);
blitFunction(dst, src);
destSurface->unlockInternal();
renderTarget->unlockInternal();
return true;
}
void Device::setDepthStencilSurface(Image *depthStencil)
{
TRACE("Image *newDepthStencil = 0x%0.8p", depthStencil);
if(this->depthStencil == depthStencil)
{
return;
}
if(depthStencil)
{
depthStencil->bind();
}
if(this->depthStencil)
{
this->depthStencil->unbind();
}
this->depthStencil = depthStencil;
setDepthStencil(depthStencil);
}
void Device::setPixelShader(PixelShader *pixelShader)
{
TRACE("PixelShader *shader = 0x%0.8p", pixelShader);
this->pixelShader = pixelShader;
pixelShaderDirty = true;
}
void Device::setPixelShaderConstantF(unsigned int startRegister, const float *constantData, unsigned int count)
{
TRACE("unsigned int startRegister = %d, const int *constantData = 0x%0.8p, unsigned int count = %d", startRegister, constantData, count);
for(unsigned int i = 0; i < count && startRegister + i < 224; i++)
{
pixelShaderConstantF[startRegister + i][0] = constantData[i * 4 + 0];
pixelShaderConstantF[startRegister + i][1] = constantData[i * 4 + 1];
pixelShaderConstantF[startRegister + i][2] = constantData[i * 4 + 2];
pixelShaderConstantF[startRegister + i][3] = constantData[i * 4 + 3];
}
pixelShaderConstantsFDirty = max(startRegister + count, pixelShaderConstantsFDirty);
pixelShaderDirty = true; // Reload DEF constants
}
void Device::setScissorEnable(bool enable)
{
scissorEnable = enable;
}
void Device::setRenderTarget(Image *iRenderTarget)
{
TRACE("Image *newRenderTarget = 0x%0.8p", iRenderTarget);
Image *renderTarget = static_cast<Image*>(iRenderTarget);
if(renderTarget)
{
renderTarget->bind();
}
if(this->renderTarget)
{
this->renderTarget->unbind();
}
this->renderTarget = renderTarget;
if(renderTarget)
{
// Reset viewport to size of current render target
viewport.x = 0;
viewport.y = 0;
viewport.width = renderTarget->getWidth();
viewport.height = renderTarget->getHeight();
viewport.minZ = 0;
viewport.maxZ = 1;
// Reset scissor rectangle to size of current render target
scissorRect.left = 0;
scissorRect.top = 0;
scissorRect.right = renderTarget->getWidth();
scissorRect.bottom = renderTarget->getHeight();
}
Renderer::setRenderTarget(0, renderTarget);
}
void Device::setScissorRect(const sw::Rect &rect)
{
TRACE("const sw::Rect *rect = 0x%0.8p", rect);
scissorRect = rect;
}
void Device::setVertexShader(VertexShader *vertexShader)
{
TRACE("VertexShader *shader = 0x%0.8p", vertexShader);
this->vertexShader = vertexShader;
vertexShaderDirty = true;
}
void Device::setVertexShaderConstantF(unsigned int startRegister, const float *constantData, unsigned int count)
{
TRACE("unsigned int startRegister = %d, const int *constantData = 0x%0.8p, unsigned int count = %d", startRegister, constantData, count);
for(unsigned int i = 0; i < count && startRegister + i < 256; i++)
{
vertexShaderConstantF[startRegister + i][0] = constantData[i * 4 + 0];
vertexShaderConstantF[startRegister + i][1] = constantData[i * 4 + 1];
vertexShaderConstantF[startRegister + i][2] = constantData[i * 4 + 2];
vertexShaderConstantF[startRegister + i][3] = constantData[i * 4 + 3];
}
vertexShaderConstantsFDirty = max(startRegister + count, vertexShaderConstantsFDirty);
vertexShaderDirty = true; // Reload DEF constants
}
void Device::setViewport(const Viewport &viewport)
{
TRACE("const Viewport *viewport = 0x%0.8p", viewport);
this->viewport = viewport;
}
bool Device::stretchRect(Image *sourceSurface, const sw::Rect *sourceRect, Image *destSurface, const sw::Rect *destRect, bool filter)
{
TRACE("Image *sourceSurface = 0x%0.8p, const sw::Rect *sourceRect = 0x%0.8p, Image *destSurface = 0x%0.8p, const sw::Rect *destRect = 0x%0.8p, bool filter = %d", sourceSurface, sourceRect, destSurface, destRect, filter);
if(!sourceSurface || !destSurface || !validRectangle(sourceRect, sourceSurface) || !validRectangle(destRect, destSurface))
{
ERR("Invalid parameters");
return false;
}
Image *source = static_cast<Image*>(sourceSurface);
Image *dest = static_cast<Image*>(destSurface);
int sWidth = source->getExternalWidth();
int sHeight = source->getExternalHeight();
int dWidth = dest->getExternalWidth();
int dHeight = dest->getExternalHeight();
Rect sRect;
Rect dRect;
if(sourceRect)
{
sRect = *sourceRect;
}
else
{
sRect.top = 0;
sRect.left = 0;
sRect.bottom = sHeight;
sRect.right = sWidth;
}
if(destRect)
{
dRect = *destRect;
}
else
{
dRect.top = 0;
dRect.left = 0;
dRect.bottom = dHeight;
dRect.right = dWidth;
}
bool scaling = (sRect.right - sRect.left != dRect.right - dRect.left) || (sRect.bottom - sRect.top != dRect.bottom - dRect.top);
bool equalFormats = source->getInternalFormat() == dest->getInternalFormat();
bool depthStencil = Image::isDepth(source->getInternalFormat()) || Image::isStencil(source->getInternalFormat());
bool alpha0xFF = false;
if((source->getInternalFormat() == FORMAT_A8R8G8B8 && dest->getInternalFormat() == FORMAT_X8R8G8B8) ||
(source->getInternalFormat() == FORMAT_X8R8G8B8 && dest->getInternalFormat() == FORMAT_A8R8G8B8))
{
equalFormats = true;
alpha0xFF = true;
}
if(depthStencil) // Copy entirely, internally // FIXME: Check
{
if(source->hasDepth())
{
sw::byte *sourceBuffer = (sw::byte*)source->lockInternal(0, 0, 0, LOCK_READONLY, PUBLIC);
sw::byte *destBuffer = (sw::byte*)dest->lockInternal(0, 0, 0, LOCK_DISCARD, PUBLIC);
unsigned int width = source->getInternalWidth();
unsigned int height = source->getInternalHeight();
unsigned int pitch = source->getInternalPitchB();
for(unsigned int y = 0; y < height; y++)
{
memcpy(destBuffer, sourceBuffer, pitch); // FIXME: Only copy width * bytes
sourceBuffer += pitch;
destBuffer += pitch;
}
source->unlockInternal();
dest->unlockInternal();
}
if(source->hasStencil())
{
sw::byte *sourceBuffer = (sw::byte*)source->lockStencil(0, PUBLIC);
sw::byte *destBuffer = (sw::byte*)dest->lockStencil(0, PUBLIC);
unsigned int width = source->getInternalWidth();
unsigned int height = source->getInternalHeight();
unsigned int pitch = source->getStencilPitchB();
for(unsigned int y = 0; y < height; y++)
{
memcpy(destBuffer, sourceBuffer, pitch); // FIXME: Only copy width * bytes
sourceBuffer += pitch;
destBuffer += pitch;
}
source->unlockStencil();
dest->unlockStencil();
}
}
else if(!scaling && equalFormats)
{
unsigned char *sourceBytes = (unsigned char*)source->lockInternal(sRect.left, sRect.top, 0, LOCK_READONLY, PUBLIC);
unsigned char *destBytes = (unsigned char*)dest->lockInternal(dRect.left, dRect.top, 0, LOCK_READWRITE, PUBLIC);
unsigned int sourcePitch = source->getInternalPitchB();
unsigned int destPitch = dest->getInternalPitchB();
unsigned int width = dRect.right - dRect.left;
unsigned int height = dRect.bottom - dRect.top;
unsigned int bytes = width * Image::bytes(source->getInternalFormat());
for(unsigned int y = 0; y < height; y++)
{
memcpy(destBytes, sourceBytes, bytes);
if(alpha0xFF)
{
for(unsigned int x = 0; x < width; x++)
{
destBytes[4 * x + 3] = 0xFF;
}
}
sourceBytes += sourcePitch;
destBytes += destPitch;
}
source->unlockInternal();
dest->unlockInternal();
}
else
{
blit(source, sRect, dest, dRect, scaling && filter);
}
return true;
}
bool Device::updateSurface(Image *sourceSurface, const sw::Rect *sourceRect, Image *destinationSurface, const POINT *destPoint)
{
TRACE("Image *sourceSurface = 0x%0.8p, const sw::Rect *sourceRect = 0x%0.8p, Image *destinationSurface = 0x%0.8p, const POINT *destPoint = 0x%0.8p", sourceSurface, sourceRect, destinationSurface, destPoint);
if(!sourceSurface || !destinationSurface)
{
ERR("Invalid parameters");
return false;
}
Rect sRect;
Rect dRect;
if(sourceRect)
{
sRect.left = sourceRect->left;
sRect.top = sourceRect->top;
sRect.right = sourceRect->right;
sRect.bottom = sourceRect->bottom;
}
else
{
sRect.left = 0;
sRect.top = 0;
sRect.right = sourceSurface->getWidth();
sRect.bottom = sourceSurface->getHeight();
}
if(destPoint)
{
dRect.left = destPoint->x;
dRect.top = destPoint->y;
dRect.right = destPoint->x + sRect.right - sRect.left;
dRect.bottom = destPoint->y + sRect.bottom - sRect.top;
}
else
{
dRect.left = 0;
dRect.top = 0;
dRect.right = sRect.right - sRect.left;
dRect.bottom = sRect.bottom - sRect.top;
}
if(!validRectangle(&sRect, sourceSurface) || !validRectangle(&dRect, destinationSurface))
{
ERR("Invalid parameters");
return false;
}
int sWidth = sRect.right - sRect.left;
int sHeight = sRect.bottom - sRect.top;
int dWidth = dRect.right - dRect.left;
int dHeight = dRect.bottom - dRect.top;
if(sourceSurface->getMultiSampleDepth() > 1 ||
destinationSurface->getMultiSampleDepth() > 1 ||
sourceSurface->getInternalFormat() != destinationSurface->getInternalFormat())
{
ERR("Invalid parameters");
return false;
}
unsigned char *sourceBuffer = (unsigned char*)sourceSurface->lock(sRect.left, sRect.top, LOCK_READONLY);
unsigned char *destinationBuffer = (unsigned char*)destinationSurface->lock(dRect.left, dRect.top, LOCK_WRITEONLY);
unsigned int width;
unsigned int height;
unsigned int bytes;
switch(sourceSurface->getInternalFormat())
{
#if S3TC_SUPPORT
case FORMAT_DXT1:
case FORMAT_ATI1:
width = (dWidth + 3) / 4;
height = (dHeight + 3) / 4;
bytes = width * 8; // 64 bit per 4x4 block
break;
// case FORMAT_DXT2:
case FORMAT_DXT3:
// case FORMAT_DXT4:
case FORMAT_DXT5:
case FORMAT_ATI2:
width = (dWidth + 3) / 4;
height = (dHeight + 3) / 4;
bytes = width * 16; // 128 bit per 4x4 block
break;
#endif
default:
width = dWidth;
height = dHeight;
bytes = width * Image::bytes(sourceSurface->getInternalFormat());
}
int sourcePitch = sourceSurface->getPitch();
int destinationPitch = destinationSurface->getPitch();
#if S3TC_SUPPORT
if(sourceSurface->getInternalFormat() == FORMAT_ATI1 || sourceSurface->getInternalFormat() == FORMAT_ATI2)
{
// Make the pitch correspond to 4 rows
sourcePitch *= 4;
destinationPitch *= 4;
}
#endif
for(unsigned int y = 0; y < height; y++)
{
memcpy(destinationBuffer, sourceBuffer, bytes);
sourceBuffer += sourcePitch;
destinationBuffer += destinationPitch;
}
sourceSurface->unlock();
destinationSurface->unlock();
return true;
}
bool Device::bindResources()
{
if(!bindViewport())
{
return false; // Zero-area target region
}
bindShaderConstants();
return true;
}
void Device::bindShaderConstants()
{
if(pixelShaderDirty)
{
if(pixelShader)
{
if(pixelShaderConstantsFDirty)
{
Renderer::setPixelShaderConstantF(0, pixelShaderConstantF[0], pixelShaderConstantsFDirty);
}
Renderer::setPixelShader(pixelShader); // Loads shader constants set with DEF
pixelShaderConstantsFDirty = pixelShader->dirtyConstantsF; // Shader DEF'ed constants are dirty
}
else
{
setPixelShader(0);
}
pixelShaderDirty = false;
}
if(vertexShaderDirty)
{
if(vertexShader)
{
if(vertexShaderConstantsFDirty)
{
Renderer::setVertexShaderConstantF(0, vertexShaderConstantF[0], vertexShaderConstantsFDirty);
}
Renderer::setVertexShader(vertexShader); // Loads shader constants set with DEF
vertexShaderConstantsFDirty = vertexShader->dirtyConstantsF; // Shader DEF'ed constants are dirty
}
else
{
setVertexShader(0);
}
vertexShaderDirty = false;
}
}
bool Device::bindViewport()
{
if(viewport.width <= 0 || viewport.height <= 0)
{
return false;
}
if(scissorEnable)
{
Rect scissor = scissorRect;
long viewportLeft = viewport.x;
long viewportRight = viewport.x + viewport.width;
long viewportTop = viewport.y;
long viewportBottom = viewport.y + viewport.height;
// Intersection of scissor rectangle and viewport
if(viewportLeft > scissor.left) scissor.left = viewportLeft;
if(viewportTop > scissor.top) scissor.top = viewportTop;
if(viewportRight < scissor.right) scissor.right = viewportRight;
if(viewportBottom < scissor.bottom) scissor.bottom = viewportBottom;
if(scissor.left == scissor.right ||
scissor.top == scissor.bottom)
{
return false;
}
// Dimensions of scissor rectangle relative to viewport
float relativeLeft = (float)(scissor.left - viewportLeft) / viewport.width;
float relativeRight = (float)(scissor.right - viewportLeft) / viewport.width;
float relativeTop = (float)(scissor.top - viewportTop) / viewport.height;
float relativeBottom = (float)(scissor.bottom - viewportTop) / viewport.height;
// Transformation of clip space coordinates
float sX = 1.0f / (relativeRight - relativeLeft); // Scale
float tX = sX * ((0.5f - relativeLeft) - (relativeRight - 0.5f)); // Translate
float sY = 1.0f / (relativeBottom - relativeTop); // Scale
float tY = sY * ((0.5f - relativeTop) - (relativeBottom - 0.5f)); // Translate
// Set the new viewport
sw::Viewport view;
view.setLeft((float)scissor.left);
view.setTop((float)scissor.top);
view.setWidth((float)(scissor.right - scissor.left));
view.setHeight((float)(scissor.bottom - scissor.top));
view.setNear(viewport.minZ);
view.setFar(viewport.maxZ);
Renderer::setViewport(view);
setPostTransformEnable(true);
setPosScale(sX, sY);
setPosOffset(tX, -tY);
}
else
{
// Set viewport
sw::Viewport view;
view.setLeft((float)viewport.x);
view.setTop((float)viewport.y);
view.setWidth((float)viewport.width);
view.setHeight((float)viewport.height);
view.setNear(viewport.minZ);
view.setFar(viewport.maxZ);
Renderer::setViewport(view);
setPostTransformEnable(false);
}
return true;
}
bool Device::validRectangle(const sw::Rect *rect, Image *surface)
{
if(!rect)
{
return true;
}
if(rect->right <= rect->left || rect->bottom <= rect->top)
{
return false;
}
if(rect->left < 0 || rect->top < 0)
{
return false;
}
if(rect->right > (int)surface->getWidth() || rect->bottom > (int)surface->getHeight())
{
return false;
}
return true;
}
void Device::finish()
{
if(renderTarget)
{
renderTarget->lock(0, 0, sw::LOCK_READWRITE);
renderTarget->unlock();
}
}
}
extern "C"
{
gl::Device *createDevice()
{
sw::Context *context = new sw::Context();
if(context)
{
return new gl::Device(context);
}
return 0;
}
}