| // 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 "FrameBuffer.hpp" |
| |
| #include "Timer.hpp" |
| #include "CPUID.hpp" |
| #include "Renderer/Surface.hpp" |
| #include "Reactor/Reactor.hpp" |
| #include "Common/Debug.hpp" |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <time.h> |
| |
| #ifdef __ANDROID__ |
| #include <cutils/properties.h> |
| #endif |
| |
| #define ASYNCHRONOUS_BLIT 0 // FIXME: Currently leads to rare race conditions |
| |
| namespace sw |
| { |
| extern bool forceWindowed; |
| |
| 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(int width, int height, bool fullscreen, bool topLeftOrigin) |
| { |
| this->topLeftOrigin = topLeftOrigin; |
| |
| locked = nullptr; |
| |
| this->width = width; |
| this->height = height; |
| destFormat = FORMAT_X8R8G8B8; |
| sourceFormat = FORMAT_X8R8G8B8; |
| stride = 0; |
| |
| if(forceWindowed) |
| { |
| fullscreen = false; |
| } |
| |
| windowed = !fullscreen; |
| |
| blitFunction = nullptr; |
| blitRoutine = nullptr; |
| |
| blitState.width = 0; |
| blitState.height = 0; |
| blitState.destFormat = FORMAT_X8R8G8B8; |
| blitState.sourceFormat = FORMAT_X8R8G8B8; |
| blitState.cursorWidth = 0; |
| blitState.cursorHeight = 0; |
| |
| if(ASYNCHRONOUS_BLIT) |
| { |
| terminate = false; |
| FrameBuffer *parameters = this; |
| blitThread = new Thread(threadFunction, ¶meters); |
| } |
| } |
| |
| FrameBuffer::~FrameBuffer() |
| { |
| if(ASYNCHRONOUS_BLIT) |
| { |
| terminate = true; |
| blitEvent.signal(); |
| blitThread->join(); |
| delete blitThread; |
| } |
| |
| delete blitRoutine; |
| } |
| |
| 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->getWidth(); |
| cursorHeight = cursorImage->getHeight(); |
| } |
| 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(void *source, Format format, size_t stride) |
| { |
| if(!source) |
| { |
| return; |
| } |
| |
| if(!lock()) |
| { |
| return; |
| } |
| |
| sourceFormat = format; |
| |
| if(topLeftOrigin) |
| { |
| target = source; |
| } |
| else |
| { |
| target = (byte*)source + (height - 1) * stride; |
| } |
| |
| cursorX = cursorPositionX - cursorHotspotX; |
| cursorY = cursorPositionY - cursorHotspotY; |
| |
| if(ASYNCHRONOUS_BLIT) |
| { |
| blitEvent.signal(); |
| syncEvent.wait(); |
| } |
| else |
| { |
| copyLocked(); |
| } |
| |
| unlock(); |
| |
| profiler.nextFrame(); // Assumes every copy() is a full frame |
| } |
| |
| void FrameBuffer::copyLocked() |
| { |
| BlitState update = {}; |
| update.width = width; |
| update.height = height; |
| update.destFormat = destFormat; |
| update.sourceFormat = sourceFormat; |
| update.stride = stride; |
| update.cursorWidth = cursorWidth; |
| update.cursorHeight = cursorHeight; |
| |
| if(memcmp(&blitState, &update, sizeof(BlitState)) != 0) |
| { |
| blitState = update; |
| delete blitRoutine; |
| |
| blitRoutine = copyRoutine(blitState); |
| blitFunction = (void(*)(void*, void*))blitRoutine->getEntry(); |
| } |
| |
| blitFunction(locked, target); |
| } |
| |
| Routine *FrameBuffer::copyRoutine(const BlitState &state) |
| { |
| const int width = state.width; |
| const int height = state.height; |
| const int width2 = (state.width + 1) & ~1; |
| const int dBytes = Surface::bytes(state.destFormat); |
| const int dStride = state.stride; |
| const int sBytes = Surface::bytes(state.sourceFormat); |
| const int sStride = topLeftOrigin ? (sBytes * width2) : -(sBytes * width2); |
| |
| 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; |
| |
| switch(state.destFormat) |
| { |
| case FORMAT_X8R8G8B8: |
| case FORMAT_A8R8G8B8: |
| { |
| Int x = x0; |
| |
| switch(state.sourceFormat) |
| { |
| case FORMAT_X8R8G8B8: |
| case FORMAT_A8R8G8B8: |
| For(, x < width - 3, x += 4) |
| { |
| *Pointer<Int4>(d, 1) = *Pointer<Int4>(s, sStride % 16 ? 1 : 16); |
| |
| s += 4 * sBytes; |
| d += 4 * dBytes; |
| } |
| break; |
| case FORMAT_X8B8G8R8: |
| case FORMAT_A8B8G8R8: |
| For(, x < width - 3, x += 4) |
| { |
| Int4 bgra = *Pointer<Int4>(s, sStride % 16 ? 1 : 16); |
| |
| *Pointer<Int4>(d, 1) = ((bgra & Int4(0x00FF0000)) >> 16) | |
| ((bgra & Int4(0x000000FF)) << 16) | |
| (bgra & Int4(0xFF00FF00)); |
| |
| s += 4 * sBytes; |
| d += 4 * dBytes; |
| } |
| break; |
| case FORMAT_A16B16G16R16: |
| For(, x < width - 1, x += 2) |
| { |
| UShort4 c0 = As<UShort4>(Swizzle(*Pointer<Short4>(s + 0), 0xC6)) >> 8; |
| UShort4 c1 = As<UShort4>(Swizzle(*Pointer<Short4>(s + 8), 0xC6)) >> 8; |
| |
| *Pointer<Int2>(d) = As<Int2>(Pack(c0, c1)); |
| |
| s += 2 * sBytes; |
| d += 2 * dBytes; |
| } |
| break; |
| case FORMAT_R5G6B5: |
| For(, x < width, x++) |
| { |
| Int rgb = Int(*Pointer<Short>(s)); |
| |
| *Pointer<Int>(d) = 0xFF000000 | |
| ((rgb & 0xF800) << 8) | ((rgb & 0xE01F) << 3) | |
| ((rgb & 0x07E0) << 5) | ((rgb & 0x0600) >> 1) | |
| ((rgb & 0x001C) >> 2); |
| |
| s += sBytes; |
| d += dBytes; |
| } |
| break; |
| default: |
| ASSERT(false); |
| break; |
| } |
| |
| For(, x < width, x++) |
| { |
| switch(state.sourceFormat) |
| { |
| case FORMAT_X8R8G8B8: |
| case FORMAT_A8R8G8B8: |
| *Pointer<Int>(d) = *Pointer<Int>(s); |
| break; |
| case FORMAT_X8B8G8R8: |
| case FORMAT_A8B8G8R8: |
| { |
| Int rgba = *Pointer<Int>(s); |
| |
| *Pointer<Int>(d) = ((rgba & Int(0x00FF0000)) >> 16) | |
| ((rgba & Int(0x000000FF)) << 16) | |
| (rgba & Int(0xFF00FF00)); |
| } |
| break; |
| case FORMAT_A16B16G16R16: |
| { |
| UShort4 c = As<UShort4>(Swizzle(*Pointer<Short4>(s), 0xC6)) >> 8; |
| |
| *Pointer<Int>(d) = Int(As<Int2>(Pack(c, c))); |
| } |
| break; |
| case FORMAT_R5G6B5: |
| { |
| Int rgb = Int(*Pointer<Short>(s)); |
| |
| *Pointer<Int>(d) = 0xFF000000 | |
| ((rgb & 0xF800) << 8) | ((rgb & 0xE01F) << 3) | |
| ((rgb & 0x07E0) << 5) | ((rgb & 0x0600) >> 1) | |
| ((rgb & 0x001C) >> 2); |
| } |
| break; |
| default: |
| ASSERT(false); |
| break; |
| } |
| |
| s += sBytes; |
| d += dBytes; |
| } |
| } |
| break; |
| case FORMAT_X8B8G8R8: |
| case FORMAT_A8B8G8R8: |
| case FORMAT_SRGB8_X8: |
| case FORMAT_SRGB8_A8: |
| { |
| Int x = x0; |
| |
| switch(state.sourceFormat) |
| { |
| case FORMAT_X8B8G8R8: |
| case FORMAT_A8B8G8R8: |
| For(, x < width - 3, x += 4) |
| { |
| *Pointer<Int4>(d, 1) = *Pointer<Int4>(s, sStride % 16 ? 1 : 16); |
| |
| s += 4 * sBytes; |
| d += 4 * dBytes; |
| } |
| break; |
| case FORMAT_X8R8G8B8: |
| case FORMAT_A8R8G8B8: |
| For(, x < width - 3, x += 4) |
| { |
| Int4 bgra = *Pointer<Int4>(s, sStride % 16 ? 1 : 16); |
| |
| *Pointer<Int4>(d, 1) = ((bgra & Int4(0x00FF0000)) >> 16) | |
| ((bgra & Int4(0x000000FF)) << 16) | |
| (bgra & Int4(0xFF00FF00)); |
| |
| s += 4 * sBytes; |
| d += 4 * dBytes; |
| } |
| break; |
| case FORMAT_A16B16G16R16: |
| For(, x < width - 1, x += 2) |
| { |
| UShort4 c0 = *Pointer<UShort4>(s + 0) >> 8; |
| UShort4 c1 = *Pointer<UShort4>(s + 8) >> 8; |
| |
| *Pointer<Int2>(d) = As<Int2>(Pack(c0, c1)); |
| |
| s += 2 * sBytes; |
| d += 2 * dBytes; |
| } |
| break; |
| case FORMAT_R5G6B5: |
| For(, x < width, x++) |
| { |
| Int rgb = Int(*Pointer<Short>(s)); |
| |
| *Pointer<Int>(d) = 0xFF000000 | |
| ((rgb & 0x001F) << 19) | ((rgb & 0x001C) << 14) | |
| ((rgb & 0x07E0) << 5) | ((rgb & 0x0600) >> 1) | |
| ((rgb & 0xF800) >> 8) | ((rgb & 0xE000) >> 13); |
| |
| s += sBytes; |
| d += dBytes; |
| } |
| break; |
| default: |
| ASSERT(false); |
| break; |
| } |
| |
| For(, x < width, x++) |
| { |
| switch(state.sourceFormat) |
| { |
| case FORMAT_X8B8G8R8: |
| case FORMAT_A8B8G8R8: |
| *Pointer<Int>(d) = *Pointer<Int>(s); |
| break; |
| case FORMAT_X8R8G8B8: |
| case FORMAT_A8R8G8B8: |
| { |
| Int bgra = *Pointer<Int>(s); |
| *Pointer<Int>(d) = ((bgra & Int(0x00FF0000)) >> 16) | |
| ((bgra & Int(0x000000FF)) << 16) | |
| (bgra & Int(0xFF00FF00)); |
| } |
| break; |
| case FORMAT_A16B16G16R16: |
| { |
| UShort4 c = *Pointer<UShort4>(s) >> 8; |
| |
| *Pointer<Int>(d) = Int(As<Int2>(Pack(c, c))); |
| } |
| break; |
| case FORMAT_R5G6B5: |
| { |
| Int rgb = Int(*Pointer<Short>(s)); |
| |
| *Pointer<Int>(d) = 0xFF000000 | |
| ((rgb & 0x001F) << 19) | ((rgb & 0x001C) << 14) | |
| ((rgb & 0x07E0) << 5) | ((rgb & 0x0600) >> 1) | |
| ((rgb & 0xF800) >> 8) | ((rgb & 0xE000) >> 13); |
| } |
| break; |
| default: |
| ASSERT(false); |
| break; |
| } |
| |
| s += sBytes; |
| d += dBytes; |
| } |
| } |
| break; |
| case FORMAT_R8G8B8: |
| { |
| For(Int x = x0, x < width, x++) |
| { |
| switch(state.sourceFormat) |
| { |
| case FORMAT_X8R8G8B8: |
| case FORMAT_A8R8G8B8: |
| *Pointer<Byte>(d + 0) = *Pointer<Byte>(s + 0); |
| *Pointer<Byte>(d + 1) = *Pointer<Byte>(s + 1); |
| *Pointer<Byte>(d + 2) = *Pointer<Byte>(s + 2); |
| break; |
| case FORMAT_X8B8G8R8: |
| case FORMAT_A8B8G8R8: |
| *Pointer<Byte>(d + 0) = *Pointer<Byte>(s + 2); |
| *Pointer<Byte>(d + 1) = *Pointer<Byte>(s + 1); |
| *Pointer<Byte>(d + 2) = *Pointer<Byte>(s + 0); |
| break; |
| case FORMAT_A16B16G16R16: |
| *Pointer<Byte>(d + 0) = *Pointer<Byte>(s + 5); |
| *Pointer<Byte>(d + 1) = *Pointer<Byte>(s + 3); |
| *Pointer<Byte>(d + 2) = *Pointer<Byte>(s + 1); |
| break; |
| case FORMAT_R5G6B5: |
| { |
| Int rgb = Int(*Pointer<Short>(s)); |
| |
| *Pointer<Byte>(d + 0) = Byte(((rgb & 0x001F) << 3) | ((rgb & 0x001C) >> 2)); |
| *Pointer<Byte>(d + 1) = Byte(((rgb & 0x07E0) << 5) | ((rgb & 0x0600) >> 1)); |
| *Pointer<Byte>(d + 2) = Byte(((rgb & 0xF800) << 8) | ((rgb & 0xE000) << 3)); |
| } |
| break; |
| default: |
| ASSERT(false); |
| break; |
| } |
| |
| s += sBytes; |
| d += dBytes; |
| } |
| } |
| break; |
| case FORMAT_R5G6B5: |
| { |
| For(Int x = x0, x < width, x++) |
| { |
| switch(state.sourceFormat) |
| { |
| case FORMAT_X8R8G8B8: |
| case FORMAT_A8R8G8B8: |
| { |
| Int c = *Pointer<Int>(s); |
| |
| *Pointer<Short>(d) = Short((c & 0x00F80000) >> 8 | |
| (c & 0x0000FC00) >> 5 | |
| (c & 0x000000F8) >> 3); |
| } |
| break; |
| case FORMAT_X8B8G8R8: |
| case FORMAT_A8B8G8R8: |
| { |
| Int c = *Pointer<Int>(s); |
| |
| *Pointer<Short>(d) = Short((c & 0x00F80000) >> 19 | |
| (c & 0x0000FC00) >> 5 | |
| (c & 0x000000F8) << 8); |
| } |
| break; |
| case FORMAT_A16B16G16R16: |
| { |
| UShort4 cc = *Pointer<UShort4>(s) >> 8; |
| Int c = Int(As<Int2>(Pack(cc, cc))); |
| |
| *Pointer<Short>(d) = Short((c & 0x00F80000) >> 19 | |
| (c & 0x0000FC00) >> 5 | |
| (c & 0x000000F8) << 8); |
| } |
| break; |
| case FORMAT_R5G6B5: |
| *Pointer<Short>(d) = *Pointer<Short>(s); |
| break; |
| default: |
| ASSERT(false); |
| break; |
| } |
| |
| s += sBytes; |
| d += dBytes; |
| } |
| } |
| break; |
| default: |
| ASSERT(false); |
| break; |
| } |
| } |
| |
| 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 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)); |
| |
| switch(state.sourceFormat) |
| { |
| case FORMAT_X8R8G8B8: |
| case FORMAT_A8R8G8B8: |
| c2 = UnpackLow(As<Byte8>(c2), *Pointer<Byte8>(s)); |
| break; |
| case FORMAT_X8B8G8R8: |
| case FORMAT_A8B8G8R8: |
| c2 = Swizzle(UnpackLow(As<Byte8>(c2), *Pointer<Byte8>(s)), 0xC6); |
| break; |
| case FORMAT_A16B16G16R16: |
| c2 = Swizzle(*Pointer<Short4>(s), 0xC6); |
| break; |
| case FORMAT_R5G6B5: |
| { |
| Int rgb(*Pointer<Short>(s)); |
| rgb = 0xFF000000 | |
| ((rgb & 0xF800) << 8) | ((rgb & 0xE01F) << 3) | |
| ((rgb & 0x07E0) << 5) | ((rgb & 0x0600) >> 1) | |
| ((rgb & 0x001C) >> 2); |
| c2 = Unpack(As<Byte4>(rgb)); |
| } |
| break; |
| default: |
| ASSERT(false); |
| break; |
| } |
| |
| 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; |
| |
| switch(state.destFormat) |
| { |
| case FORMAT_X8R8G8B8: |
| case FORMAT_A8R8G8B8: |
| *Pointer<Byte4>(d) = Byte4(Pack(As<UShort4>(c1), As<UShort4>(c1))); |
| break; |
| case FORMAT_X8B8G8R8: |
| case FORMAT_A8B8G8R8: |
| case FORMAT_SRGB8_X8: |
| case FORMAT_SRGB8_A8: |
| { |
| c1 = Swizzle(c1, 0xC6); |
| |
| *Pointer<Byte4>(d) = Byte4(Pack(As<UShort4>(c1), As<UShort4>(c1))); |
| } |
| break; |
| case FORMAT_R8G8B8: |
| { |
| Int c = Int(As<Int2>(Pack(As<UShort4>(c1), As<UShort4>(c1)))); |
| |
| *Pointer<Byte>(d + 0) = Byte(c >> 0); |
| *Pointer<Byte>(d + 1) = Byte(c >> 8); |
| *Pointer<Byte>(d + 2) = Byte(c >> 16); |
| } |
| break; |
| case FORMAT_R5G6B5: |
| { |
| Int c = Int(As<Int2>(Pack(As<UShort4>(c1), As<UShort4>(c1)))); |
| |
| *Pointer<Short>(d) = Short((c & 0x00F80000) >> 8 | |
| (c & 0x0000FC00) >> 5 | |
| (c & 0x000000F8) >> 3); |
| } |
| break; |
| default: |
| ASSERT(false); |
| break; |
| } |
| } |
| |
| void FrameBuffer::threadFunction(void *parameters) |
| { |
| FrameBuffer *frameBuffer = *static_cast<FrameBuffer**>(parameters); |
| |
| while(!frameBuffer->terminate) |
| { |
| frameBuffer->blitEvent.wait(); |
| |
| if(!frameBuffer->terminate) |
| { |
| frameBuffer->copyLocked(); |
| |
| frameBuffer->syncEvent.signal(); |
| } |
| } |
| } |
| } |