// 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 "Device/Surface.hpp"
|
#include "Reactor/Reactor.hpp"
|
#include "System/Timer.hpp"
|
#include "System/Debug.hpp"
|
|
#include <stdio.h>
|
#include <string.h>
|
#include <time.h>
|
|
#define ASYNCHRONOUS_BLIT false // FIXME: Currently leads to rare race conditions
|
|
namespace sw
|
{
|
extern bool forceWindowed;
|
|
FrameBuffer::Cursor FrameBuffer::cursor = {};
|
bool FrameBuffer::topLeftOrigin = false;
|
|
FrameBuffer::FrameBuffer(int width, int height, bool fullscreen, bool topLeftOrigin)
|
{
|
this->topLeftOrigin = topLeftOrigin;
|
|
framebuffer = nullptr;
|
|
this->width = width;
|
this->height = height;
|
format = VK_FORMAT_B8G8R8A8_UNORM;
|
stride = 0;
|
|
windowed = !fullscreen || forceWindowed;
|
|
blitFunction = nullptr;
|
blitRoutine = nullptr;
|
blitState = {};
|
|
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;
|
}
|
|
void FrameBuffer::setCursorImage(sw::Surface *cursorImage)
|
{
|
if(cursorImage)
|
{
|
cursor.image = cursorImage->lockExternal(0, 0, 0, sw::LOCK_READONLY, sw::PUBLIC);
|
cursorImage->unlockExternal();
|
|
cursor.width = cursorImage->getWidth();
|
cursor.height = cursorImage->getHeight();
|
}
|
else
|
{
|
cursor.width = 0;
|
cursor.height = 0;
|
}
|
}
|
|
void FrameBuffer::setCursorOrigin(int x0, int y0)
|
{
|
cursor.hotspotX = x0;
|
cursor.hotspotY = y0;
|
}
|
|
void FrameBuffer::setCursorPosition(int x, int y)
|
{
|
cursor.positionX = x;
|
cursor.positionY = y;
|
}
|
|
void FrameBuffer::copy(sw::Surface *source)
|
{
|
if(!source)
|
{
|
return;
|
}
|
|
if(!lock())
|
{
|
return;
|
}
|
|
int sourceStride = source->getInternalPitchB();
|
|
updateState = {};
|
updateState.width = width;
|
updateState.height = height;
|
updateState.destFormat = format;
|
updateState.destStride = stride;
|
updateState.sourceFormat = source->getInternalFormat();
|
updateState.sourceStride = topLeftOrigin ? sourceStride : -sourceStride;
|
updateState.cursorWidth = cursor.width;
|
updateState.cursorHeight = cursor.height;
|
|
renderbuffer = source->lockInternal(0, 0, 0, sw::LOCK_READONLY, sw::PUBLIC);
|
|
if(!topLeftOrigin)
|
{
|
renderbuffer = (byte*)renderbuffer + (height - 1) * sourceStride;
|
}
|
|
cursor.x = cursor.positionX - cursor.hotspotX;
|
cursor.y = cursor.positionY - cursor.hotspotY;
|
|
if(ASYNCHRONOUS_BLIT)
|
{
|
blitEvent.signal();
|
syncEvent.wait();
|
}
|
else
|
{
|
copyLocked();
|
}
|
|
source->unlockInternal();
|
unlock();
|
|
profiler.nextFrame(); // Assumes every copy() is a full frame
|
}
|
|
void FrameBuffer::copyLocked()
|
{
|
if(memcmp(&blitState, &updateState, sizeof(BlitState)) != 0)
|
{
|
blitState = updateState;
|
delete blitRoutine;
|
|
blitRoutine = copyRoutine(blitState);
|
blitFunction = (void(*)(void*, void*, Cursor*))blitRoutine->getEntry();
|
}
|
|
blitFunction(framebuffer, renderbuffer, &cursor);
|
}
|
|
Routine *FrameBuffer::copyRoutine(const BlitState &state)
|
{
|
const int width = state.width;
|
const int height = state.height;
|
const int dBytes = Surface::bytes(state.destFormat);
|
const int dStride = state.destStride;
|
const int sBytes = Surface::bytes(state.sourceFormat);
|
const int sStride = state.sourceStride;
|
|
Function<Void(Pointer<Byte>, Pointer<Byte>, Pointer<Byte>)> function;
|
{
|
Pointer<Byte> dst(function.Arg<0>());
|
Pointer<Byte> src(function.Arg<1>());
|
Pointer<Byte> cursor(function.Arg<2>());
|
|
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 VK_FORMAT_B8G8R8A8_UNORM:
|
{
|
Int x = x0;
|
|
switch(state.sourceFormat)
|
{
|
case VK_FORMAT_B8G8R8A8_UNORM:
|
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 VK_FORMAT_R8G8B8A8_UNORM:
|
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 VK_FORMAT_R16G16B16A16_UNORM:
|
For(, x < width - 1, x += 2)
|
{
|
Short4 c0 = As<UShort4>(Swizzle(*Pointer<Short4>(s + 0), 0xC6)) >> 8;
|
Short4 c1 = As<UShort4>(Swizzle(*Pointer<Short4>(s + 8), 0xC6)) >> 8;
|
|
*Pointer<Int2>(d) = As<Int2>(PackUnsigned(c0, c1));
|
|
s += 2 * sBytes;
|
d += 2 * dBytes;
|
}
|
break;
|
case VK_FORMAT_R5G6B5_UNORM_PACK16:
|
For(, x < width - 3, x += 4)
|
{
|
Int4 rgb = Int4(*Pointer<Short4>(s));
|
|
*Pointer<Int4>(d) = (((rgb & Int4(0xF800)) << 8) | ((rgb & Int4(0xE01F)) << 3)) |
|
(((rgb & Int4(0x07E0)) << 5) | ((rgb & Int4(0x0600)) >> 1)) |
|
(((rgb & Int4(0x001C)) >> 2) | Int4(0xFF000000));
|
|
s += 4 * sBytes;
|
d += 4 * dBytes;
|
}
|
break;
|
default:
|
ASSERT(false);
|
break;
|
}
|
|
For(, x < width, x++)
|
{
|
switch(state.sourceFormat)
|
{
|
case VK_FORMAT_B8G8R8A8_UNORM:
|
*Pointer<Int>(d) = *Pointer<Int>(s);
|
break;
|
case VK_FORMAT_R8G8B8A8_UNORM:
|
{
|
Int rgba = *Pointer<Int>(s);
|
|
*Pointer<Int>(d) = ((rgba & Int(0x00FF0000)) >> 16) |
|
((rgba & Int(0x000000FF)) << 16) |
|
(rgba & Int(0xFF00FF00));
|
}
|
break;
|
case VK_FORMAT_R16G16B16A16_UNORM:
|
{
|
Short4 c = As<UShort4>(Swizzle(*Pointer<Short4>(s), 0xC6)) >> 8;
|
|
*Pointer<Int>(d) = Int(As<Int2>(PackUnsigned(c, c)));
|
}
|
break;
|
case VK_FORMAT_R5G6B5_UNORM_PACK16:
|
{
|
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 VK_FORMAT_R8G8B8A8_UNORM:
|
case VK_FORMAT_R8G8B8A8_SRGB:
|
{
|
Int x = x0;
|
|
switch(state.sourceFormat)
|
{
|
case VK_FORMAT_R8G8B8A8_UNORM:
|
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 VK_FORMAT_B8G8R8A8_UNORM:
|
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 VK_FORMAT_R16G16B16A16_UNORM:
|
For(, x < width - 1, x += 2)
|
{
|
Short4 c0 = *Pointer<UShort4>(s + 0) >> 8;
|
Short4 c1 = *Pointer<UShort4>(s + 8) >> 8;
|
|
*Pointer<Int2>(d) = As<Int2>(PackUnsigned(c0, c1));
|
|
s += 2 * sBytes;
|
d += 2 * dBytes;
|
}
|
break;
|
case VK_FORMAT_R5G6B5_UNORM_PACK16:
|
For(, x < width - 3, x += 4)
|
{
|
Int4 rgb = Int4(*Pointer<Short4>(s));
|
|
*Pointer<Int4>(d) = Int4(0xFF000000) |
|
(((rgb & Int4(0x001F)) << 19) | ((rgb & Int4(0x001C)) << 14)) |
|
(((rgb & Int4(0x07E0)) << 5) | ((rgb & Int4(0x0600)) >> 1)) |
|
(((rgb & Int4(0xF800)) >> 8) | ((rgb & Int4(0xE000)) >> 13));
|
|
s += 4 * sBytes;
|
d += 4 * dBytes;
|
}
|
break;
|
default:
|
ASSERT(false);
|
break;
|
}
|
|
For(, x < width, x++)
|
{
|
switch(state.sourceFormat)
|
{
|
case VK_FORMAT_R8G8B8A8_UNORM:
|
*Pointer<Int>(d) = *Pointer<Int>(s);
|
break;
|
case VK_FORMAT_B8G8R8A8_UNORM:
|
{
|
Int bgra = *Pointer<Int>(s);
|
*Pointer<Int>(d) = ((bgra & Int(0x00FF0000)) >> 16) |
|
((bgra & Int(0x000000FF)) << 16) |
|
(bgra & Int(0xFF00FF00));
|
}
|
break;
|
case VK_FORMAT_R16G16B16A16_UNORM:
|
{
|
Short4 c = *Pointer<UShort4>(s) >> 8;
|
|
*Pointer<Int>(d) = Int(As<Int2>(PackUnsigned(c, c)));
|
}
|
break;
|
case VK_FORMAT_R5G6B5_UNORM_PACK16:
|
{
|
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 VK_FORMAT_R5G6B5_UNORM_PACK16:
|
{
|
For(Int x = x0, x < width, x++)
|
{
|
switch(state.sourceFormat)
|
{
|
case VK_FORMAT_B8G8R8A8_UNORM:
|
{
|
Int c = *Pointer<Int>(s);
|
|
*Pointer<Short>(d) = Short((c & 0x00F80000) >> 8 |
|
(c & 0x0000FC00) >> 5 |
|
(c & 0x000000F8) >> 3);
|
}
|
break;
|
case VK_FORMAT_R8G8B8A8_UNORM:
|
{
|
Int c = *Pointer<Int>(s);
|
|
*Pointer<Short>(d) = Short((c & 0x00F80000) >> 19 |
|
(c & 0x0000FC00) >> 5 |
|
(c & 0x000000F8) << 8);
|
}
|
break;
|
case VK_FORMAT_R16G16B16A16_UNORM:
|
{
|
Short4 cc = *Pointer<UShort4>(s) >> 8;
|
Int c = Int(As<Int2>(PackUnsigned(cc, cc)));
|
|
*Pointer<Short>(d) = Short((c & 0x00F80000) >> 19 |
|
(c & 0x0000FC00) >> 5 |
|
(c & 0x000000F8) << 8);
|
}
|
break;
|
case VK_FORMAT_R5G6B5_UNORM_PACK16:
|
*Pointer<Short>(d) = *Pointer<Short>(s);
|
break;
|
default:
|
ASSERT(false);
|
break;
|
}
|
|
s += sBytes;
|
d += dBytes;
|
}
|
}
|
break;
|
default:
|
ASSERT(false);
|
break;
|
}
|
}
|
|
if(state.cursorWidth > 0 && state.cursorHeight > 0)
|
{
|
Int x0 = *Pointer<Int>(cursor + OFFSET(Cursor,x));
|
Int y0 = *Pointer<Int>(cursor + OFFSET(Cursor,y));
|
|
For(Int y1 = 0, y1 < state.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 + OFFSET(Cursor,image)) + y1 * state.cursorWidth * 4;
|
|
For(Int x1 = 0, x1 < state.cursorWidth, x1++)
|
{
|
Int x = x0 + x1;
|
|
If(x >= 0 && x < width)
|
{
|
blend(state, d, s, c);
|
}
|
|
c += 4;
|
s += sBytes;
|
d += dBytes;
|
}
|
}
|
}
|
}
|
}
|
|
return function("FrameBuffer");
|
}
|
|
void FrameBuffer::blend(const BlitState &state, const Pointer<Byte> &d, const Pointer<Byte> &s, const Pointer<Byte> &c)
|
{
|
Short4 c1;
|
Short4 c2;
|
|
c1 = Unpack(*Pointer<Byte4>(c));
|
|
switch(state.sourceFormat)
|
{
|
case VK_FORMAT_B8G8R8A8_UNORM:
|
c2 = Unpack(*Pointer<Byte4>(s));
|
break;
|
case VK_FORMAT_R8G8B8A8_UNORM:
|
c2 = Swizzle(Unpack(*Pointer<Byte4>(s)), 0xC6);
|
break;
|
case VK_FORMAT_R16G16B16A16_UNORM:
|
c2 = Swizzle(*Pointer<Short4>(s), 0xC6);
|
break;
|
case VK_FORMAT_R5G6B5_UNORM_PACK16:
|
{
|
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 VK_FORMAT_B8G8R8A8_UNORM:
|
*Pointer<Byte4>(d) = Byte4(PackUnsigned(c1, c1));
|
break;
|
case VK_FORMAT_R8G8B8A8_UNORM:
|
case VK_FORMAT_R8G8B8A8_SRGB:
|
{
|
c1 = Swizzle(c1, 0xC6);
|
|
*Pointer<Byte4>(d) = Byte4(PackUnsigned(c1, c1));
|
}
|
break;
|
case VK_FORMAT_R5G6B5_UNORM_PACK16:
|
{
|
Int c = Int(As<Int2>(PackUnsigned(c1, 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();
|
}
|
}
|
}
|
}
|