| // Copyright 2016 The SwiftShader Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "Device.hpp" |
| |
| #include "common/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/Memory.hpp" |
| #include "Common/Timer.hpp" |
| #include "../common/debug.h" |
| |
| namespace es1 |
| { |
| using namespace sw; |
| |
| Device::Device(Context *context) : Renderer(context, OpenGL, true), context(context) |
| { |
| renderTarget = nullptr; |
| depthBuffer = nullptr; |
| stencilBuffer = nullptr; |
| |
| setDepthBufferEnable(true); |
| setFillMode(FILL_SOLID); |
| setShadingMode(SHADING_GOURAUD); |
| setDepthWriteEnable(true); |
| setAlphaTestEnable(false); |
| setSourceBlendFactor(BLEND_ONE); |
| setDestBlendFactor(BLEND_ZERO); |
| setCullMode(CULL_COUNTERCLOCKWISE); |
| setDepthCompare(DEPTH_LESSEQUAL); |
| setAlphaReference(0.0f); |
| setAlphaCompare(ALPHA_ALWAYS); |
| setAlphaBlendEnable(false); |
| setFogEnable(false); |
| setSpecularEnable(true); |
| setLocalViewer(false); |
| setFogColor(0); |
| setPixelFogMode(FOG_NONE); |
| setFogStart(0.0f); |
| setFogEnd(1.0f); |
| setFogDensity(1.0f); |
| setRangeFogEnable(false); |
| setStencilEnable(false); |
| setStencilFailOperation(OPERATION_KEEP); |
| setStencilZFailOperation(OPERATION_KEEP); |
| setStencilPassOperation(OPERATION_KEEP); |
| setStencilCompare(STENCIL_ALWAYS); |
| setStencilReference(0); |
| setStencilMask(0xFFFFFFFF); |
| setStencilWriteMask(0xFFFFFFFF); |
| setVertexFogMode(FOG_NONE); |
| setClipFlags(0); |
| setPointSize(1.0f); |
| setPointSizeMin(0.125f); |
| setPointSizeMax(8192.0f); |
| setColorWriteMask(0, 0x0000000F); |
| setBlendOperation(BLENDOP_ADD); |
| scissorEnable = false; |
| setSlopeDepthBias(0.0f); |
| setTwoSidedStencil(false); |
| setStencilFailOperationCCW(OPERATION_KEEP); |
| setStencilZFailOperationCCW(OPERATION_KEEP); |
| setStencilPassOperationCCW(OPERATION_KEEP); |
| setStencilCompareCCW(STENCIL_ALWAYS); |
| setColorWriteMask(1, 0x0000000F); |
| setColorWriteMask(2, 0x0000000F); |
| setColorWriteMask(3, 0x0000000F); |
| setBlendConstant(0xFFFFFFFF); |
| setWriteSRGB(false); |
| setDepthBias(0.0f); |
| setSeparateAlphaBlendEnable(false); |
| setSourceBlendFactorAlpha(BLEND_ONE); |
| setDestBlendFactorAlpha(BLEND_ZERO); |
| setBlendOperationAlpha(BLENDOP_ADD); |
| setPointSpriteEnable(true); |
| |
| 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); |
| } |
| } |
| |
| Device::~Device() |
| { |
| if(renderTarget) |
| { |
| renderTarget->release(); |
| renderTarget = nullptr; |
| } |
| |
| if(depthBuffer) |
| { |
| depthBuffer->release(); |
| depthBuffer = nullptr; |
| } |
| |
| if(stencilBuffer) |
| { |
| stencilBuffer->release(); |
| stencilBuffer = nullptr; |
| } |
| |
| delete context; |
| } |
| |
| // This object has to be mem aligned |
| void* Device::operator new(size_t size) |
| { |
| ASSERT(size == sizeof(Device)); // This operator can't be called from a derived class |
| return sw::allocate(sizeof(Device), 16); |
| } |
| |
| void Device::operator delete(void * mem) |
| { |
| sw::deallocate(mem); |
| } |
| |
| void Device::clearColor(float red, float green, float blue, float alpha, unsigned int rgbaMask) |
| { |
| if(!renderTarget || !rgbaMask) |
| { |
| return; |
| } |
| |
| float rgba[4]; |
| rgba[0] = red; |
| rgba[1] = green; |
| rgba[2] = blue; |
| rgba[3] = alpha; |
| |
| sw::Rect clearRect = renderTarget->getRect(); |
| |
| if(scissorEnable) |
| { |
| clearRect.clip(scissorRect.x0, scissorRect.y0, scissorRect.x1, scissorRect.y1); |
| } |
| |
| clear(rgba, FORMAT_A32B32G32R32F, renderTarget, clearRect, rgbaMask); |
| } |
| |
| void Device::clearDepth(float z) |
| { |
| if(!depthBuffer) |
| { |
| return; |
| } |
| |
| z = clamp01(z); |
| sw::Rect clearRect = depthBuffer->getRect(); |
| |
| if(scissorEnable) |
| { |
| clearRect.clip(scissorRect.x0, scissorRect.y0, scissorRect.x1, scissorRect.y1); |
| } |
| |
| depthBuffer->clearDepth(z, clearRect.x0, clearRect.y0, clearRect.width(), clearRect.height()); |
| } |
| |
| void Device::clearStencil(unsigned int stencil, unsigned int mask) |
| { |
| if(!stencilBuffer) |
| { |
| return; |
| } |
| |
| sw::Rect clearRect = stencilBuffer->getRect(); |
| |
| if(scissorEnable) |
| { |
| clearRect.clip(scissorRect.x0, scissorRect.y0, scissorRect.x1, scissorRect.y1); |
| } |
| |
| stencilBuffer->clearStencil(stencil, mask, clearRect.x0, clearRect.y0, clearRect.width(), clearRect.height()); |
| } |
| |
| void Device::drawIndexedPrimitive(sw::DrawType type, unsigned int indexOffset, unsigned int primitiveCount) |
| { |
| if(!bindResources() || !primitiveCount) |
| { |
| return; |
| } |
| |
| draw(type, indexOffset, primitiveCount); |
| } |
| |
| void Device::drawPrimitive(sw::DrawType type, unsigned int primitiveCount) |
| { |
| if(!bindResources() || !primitiveCount) |
| { |
| return; |
| } |
| |
| setIndexBuffer(nullptr); |
| |
| draw(type, 0, primitiveCount); |
| } |
| |
| void Device::setScissorEnable(bool enable) |
| { |
| scissorEnable = enable; |
| } |
| |
| void Device::setRenderTarget(int index, egl::Image *renderTarget) |
| { |
| if(renderTarget) |
| { |
| renderTarget->addRef(); |
| } |
| |
| if(this->renderTarget) |
| { |
| this->renderTarget->release(); |
| } |
| |
| this->renderTarget = renderTarget; |
| |
| Renderer::setRenderTarget(index, renderTarget); |
| } |
| |
| void Device::setDepthBuffer(egl::Image *depthBuffer) |
| { |
| if(this->depthBuffer == depthBuffer) |
| { |
| return; |
| } |
| |
| if(depthBuffer) |
| { |
| depthBuffer->addRef(); |
| } |
| |
| if(this->depthBuffer) |
| { |
| this->depthBuffer->release(); |
| } |
| |
| this->depthBuffer = depthBuffer; |
| |
| Renderer::setDepthBuffer(depthBuffer); |
| } |
| |
| void Device::setStencilBuffer(egl::Image *stencilBuffer) |
| { |
| if(this->stencilBuffer == stencilBuffer) |
| { |
| return; |
| } |
| |
| if(stencilBuffer) |
| { |
| stencilBuffer->addRef(); |
| } |
| |
| if(this->stencilBuffer) |
| { |
| this->stencilBuffer->release(); |
| } |
| |
| this->stencilBuffer = stencilBuffer; |
| |
| Renderer::setStencilBuffer(stencilBuffer); |
| } |
| |
| void Device::setScissorRect(const sw::Rect &rect) |
| { |
| scissorRect = rect; |
| } |
| |
| void Device::setViewport(const Viewport &viewport) |
| { |
| this->viewport = viewport; |
| } |
| |
| bool Device::stretchRect(sw::Surface *source, const sw::SliceRect *sourceRect, sw::Surface *dest, const sw::SliceRect *destRect, bool filter) |
| { |
| if(!source || !dest || !validRectangle(sourceRect, source) || !validRectangle(destRect, dest)) |
| { |
| ERR("Invalid parameters"); |
| return false; |
| } |
| |
| int sWidth = source->getWidth(); |
| int sHeight = source->getHeight(); |
| int dWidth = dest->getWidth(); |
| int dHeight = dest->getHeight(); |
| |
| SliceRect sRect; |
| SliceRect dRect; |
| |
| if(sourceRect) |
| { |
| sRect = *sourceRect; |
| } |
| else |
| { |
| sRect.y0 = 0; |
| sRect.x0 = 0; |
| sRect.y1 = sHeight; |
| sRect.x1 = sWidth; |
| } |
| |
| if(destRect) |
| { |
| dRect = *destRect; |
| } |
| else |
| { |
| dRect.y0 = 0; |
| dRect.x0 = 0; |
| dRect.y1 = dHeight; |
| dRect.x1 = dWidth; |
| } |
| |
| bool scaling = (sRect.x1 - sRect.x0 != dRect.x1 - dRect.x0) || (sRect.y1 - sRect.y0 != dRect.y1 - dRect.y0); |
| bool equalFormats = source->getInternalFormat() == dest->getInternalFormat(); |
| bool depthStencil = egl::Image::isDepth(source->getInternalFormat()) || egl::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, sRect.slice, LOCK_READONLY, PUBLIC); |
| sw::byte *destBuffer = (sw::byte*)dest->lockInternal(0, 0, dRect.slice, LOCK_DISCARD, PUBLIC); |
| |
| unsigned int width = source->getWidth(); |
| unsigned int height = source->getHeight(); |
| 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, 0, 0, PUBLIC); |
| sw::byte *destBuffer = (sw::byte*)dest->lockStencil(0, 0, 0, PUBLIC); |
| |
| unsigned int width = source->getWidth(); |
| unsigned int height = source->getHeight(); |
| 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.x0, sRect.y0, sRect.slice, LOCK_READONLY, PUBLIC); |
| unsigned char *destBytes = (unsigned char*)dest->lockInternal(dRect.x0, dRect.y0, dRect.slice, LOCK_READWRITE, PUBLIC); |
| unsigned int sourcePitch = source->getInternalPitchB(); |
| unsigned int destPitch = dest->getInternalPitchB(); |
| |
| unsigned int width = dRect.x1 - dRect.x0; |
| unsigned int height = dRect.y1 - dRect.y0; |
| unsigned int bytes = width * egl::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 |
| { |
| sw::SliceRectF sRectF((float)sRect.x0, (float)sRect.y0, (float)sRect.x1, (float)sRect.y1, sRect.slice); |
| blit(source, sRectF, dest, dRect, scaling && filter); |
| } |
| |
| return true; |
| } |
| |
| bool Device::bindResources() |
| { |
| if(!bindViewport()) |
| { |
| return false; // Zero-area target region |
| } |
| |
| return true; |
| } |
| |
| bool Device::bindViewport() |
| { |
| if(viewport.width <= 0 || viewport.height <= 0) |
| { |
| return false; |
| } |
| |
| if(scissorEnable) |
| { |
| if(scissorRect.x0 >= scissorRect.x1 || scissorRect.y0 >= scissorRect.y1) |
| { |
| return false; |
| } |
| |
| sw::Rect scissor; |
| scissor.x0 = scissorRect.x0; |
| scissor.x1 = scissorRect.x1; |
| scissor.y0 = scissorRect.y0; |
| scissor.y1 = scissorRect.y1; |
| |
| setScissor(scissor); |
| } |
| else |
| { |
| sw::Rect scissor; |
| scissor.x0 = viewport.x0; |
| scissor.x1 = viewport.x0 + viewport.width; |
| scissor.y0 = viewport.y0; |
| scissor.y1 = viewport.y0 + viewport.height; |
| |
| if(renderTarget) |
| { |
| scissor.x0 = max(scissor.x0, 0); |
| scissor.x1 = min(scissor.x1, renderTarget->getWidth()); |
| scissor.y0 = max(scissor.y0, 0); |
| scissor.y1 = min(scissor.y1, renderTarget->getHeight()); |
| } |
| |
| if(depthBuffer) |
| { |
| scissor.x0 = max(scissor.x0, 0); |
| scissor.x1 = min(scissor.x1, depthBuffer->getWidth()); |
| scissor.y0 = max(scissor.y0, 0); |
| scissor.y1 = min(scissor.y1, depthBuffer->getHeight()); |
| } |
| |
| if(stencilBuffer) |
| { |
| scissor.x0 = max(scissor.x0, 0); |
| scissor.x1 = min(scissor.x1, stencilBuffer->getWidth()); |
| scissor.y0 = max(scissor.y0, 0); |
| scissor.y1 = min(scissor.y1, stencilBuffer->getHeight()); |
| } |
| |
| setScissor(scissor); |
| } |
| |
| sw::Viewport view; |
| view.x0 = (float)viewport.x0; |
| view.y0 = (float)viewport.y0; |
| view.width = (float)viewport.width; |
| view.height = (float)viewport.height; |
| view.minZ = viewport.minZ; |
| view.maxZ = viewport.maxZ; |
| |
| Renderer::setViewport(view); |
| |
| return true; |
| } |
| |
| bool Device::validRectangle(const sw::Rect *rect, sw::Surface *surface) |
| { |
| if(!rect) |
| { |
| return true; |
| } |
| |
| if(rect->x1 <= rect->x0 || rect->y1 <= rect->y0) |
| { |
| return false; |
| } |
| |
| if(rect->x0 < 0 || rect->y0 < 0) |
| { |
| return false; |
| } |
| |
| if(rect->x1 > (int)surface->getWidth() || rect->y1 > (int)surface->getHeight()) |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void Device::finish() |
| { |
| synchronize(); |
| } |
| } |