// 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 "Direct3DSwapChain9.hpp"
#include "Direct3DDevice9.hpp"
#include "Renderer.hpp"
#include "Timer.hpp"
#include "Resource.hpp"
#include "Configurator.hpp"
#include "Debug.hpp"
#include "FrameBufferDD.hpp"
#include "FrameBufferGDI.hpp"
namespace D3D9
{
Direct3DSwapChain9::Direct3DSwapChain9(Direct3DDevice9 *device, D3DPRESENT_PARAMETERS *presentParameters) : device(device), presentParameters(*presentParameters)
{
frameBuffer = 0;
for(int i = 0; i < 3; i++)
{
backBuffer[i] = 0;
}
reset(presentParameters);
}
Direct3DSwapChain9::~Direct3DSwapChain9()
{
release();
}
long Direct3DSwapChain9::QueryInterface(const IID &iid, void **object)
{
CriticalSection cs(device);
TRACE("");
if(iid == IID_IDirect3DSwapChain9 ||
iid == IID_IUnknown)
{
AddRef();
*object = this;
return S_OK;
}
*object = 0;
return NOINTERFACE(iid);
}
unsigned long Direct3DSwapChain9::AddRef()
{
TRACE("");
return Unknown::AddRef();
}
unsigned long Direct3DSwapChain9::Release()
{
TRACE("");
return Unknown::Release();
}
long Direct3DSwapChain9::Present(const RECT *sourceRect, const RECT *destRect, HWND destWindowOverride, const RGNDATA *dirtyRegion, unsigned long flags)
{
CriticalSection cs(device);
TRACE("");
#if PERF_PROFILE
profiler.nextFrame();
#endif
#if PERF_HUD
sw::Renderer *renderer = device->renderer;
static int64_t frame = sw::Timer::ticks();
int64_t frameTime = sw::Timer::ticks() - frame;
frame = sw::Timer::ticks();
if(frameTime > 0)
{
unsigned int *frameBuffer = (unsigned int*)lockBackBuffer(0); // FIXME: Don't assume A8R8G8B8 mode
unsigned int stride = backBuffer[0]->getInternalPitchP();
int thread;
for(thread = 0; thread < renderer->getThreadCount(); thread++)
{
int64_t drawTime = renderer->getVertexTime(thread) + renderer->getSetupTime(thread) + renderer->getPixelTime(thread);
int vertexPercentage = sw::clamp((int)(100 * renderer->getVertexTime(thread) / frameTime), 0, 100);
int setupPercentage = sw::clamp((int)(100 * renderer->getSetupTime(thread) / frameTime), 0, 100);
int pixelPercentage = sw::clamp((int)(100 * renderer->getPixelTime(thread) / frameTime), 0, 100);
for(int i = 0; i < 100; i++)
{
frameBuffer[thread * stride + i] = 0x00000000;
}
unsigned int *buffer = frameBuffer;
for(int i = 0; i < vertexPercentage; i++)
{
buffer[thread * stride] = 0x000000FF;
buffer++;
}
for(int i = 0; i < setupPercentage; i++)
{
buffer[thread * stride] = 0x0000FF00;
buffer++;
}
for(int i = 0; i < pixelPercentage; i++)
{
buffer[thread * stride] = 0x00FF0000;
buffer++;
}
frameBuffer[thread * stride + 100] = 0x00FFFFFF;
}
for(int i = 0; i <= 100; i++)
{
frameBuffer[thread * stride + i] = 0x00FFFFFF;
}
unlockBackBuffer(0);
}
renderer->resetTimers();
#endif
HWND window = destWindowOverride ? destWindowOverride : presentParameters.hDeviceWindow;
POINT point;
GetCursorPos(&point);
ScreenToClient(window, &point);
frameBuffer->setCursorPosition(point.x, point.y);
if(!sourceRect && !destRect) // FIXME: More cases?
{
frameBuffer->flip(window, backBuffer[0]);
}
else // FIXME: Check for SWAPEFFECT_COPY
{
sw::Rect sRect(0, 0, 0, 0);
sw::Rect dRect(0, 0, 0, 0);
if(sourceRect)
{
sRect.x0 = sourceRect->left;
sRect.y0 = sourceRect->top;
sRect.x1 = sourceRect->right;
sRect.y1 = sourceRect->bottom;
}
if(destRect)
{
dRect.x0 = destRect->left;
dRect.y0 = destRect->top;
dRect.x1 = destRect->right;
dRect.y1 = destRect->bottom;
}
frameBuffer->blit(window, backBuffer[0], sourceRect ? &sRect : nullptr, destRect ? &dRect : nullptr);
}
return D3D_OK;
}
long Direct3DSwapChain9::GetFrontBufferData(IDirect3DSurface9 *destSurface)
{
CriticalSection cs(device);
TRACE("");
if(!destSurface)
{
return INVALIDCALL();
}
sw::Surface *dest = static_cast<Direct3DSurface9*>(destSurface);
void *buffer = dest->lockExternal(0, 0, 0, sw::LOCK_WRITEONLY, sw::PRIVATE);
frameBuffer->screenshot(buffer);
dest->unlockExternal();
return D3D_OK;
}
long Direct3DSwapChain9::GetBackBuffer(unsigned int index, D3DBACKBUFFER_TYPE type, IDirect3DSurface9 **backBuffer)
{
CriticalSection cs(device);
TRACE("");
if(!backBuffer/* || type != D3DBACKBUFFER_TYPE_MONO*/)
{
return INVALIDCALL();
}
*backBuffer = 0;
if(index >= 3 || this->backBuffer[index] == 0)
{
return INVALIDCALL();
}
*backBuffer = this->backBuffer[index];
this->backBuffer[index]->AddRef();
return D3D_OK;
}
long Direct3DSwapChain9::GetRasterStatus(D3DRASTER_STATUS *rasterStatus)
{
CriticalSection cs(device);
TRACE("");
if(!rasterStatus)
{
return INVALIDCALL();
}
bool inVerticalBlank;
unsigned int scanline;
bool supported = frameBuffer->getScanline(inVerticalBlank, scanline);
if(supported)
{
rasterStatus->InVBlank = inVerticalBlank;
rasterStatus->ScanLine = scanline;
}
else
{
return INVALIDCALL();
}
return D3D_OK;
}
long Direct3DSwapChain9::GetDisplayMode(D3DDISPLAYMODE *displayMode)
{
CriticalSection cs(device);
TRACE("");
if(!displayMode)
{
return INVALIDCALL();
}
device->getAdapterDisplayMode(D3DADAPTER_DEFAULT, displayMode);
return D3D_OK;
}
long Direct3DSwapChain9::GetDevice(IDirect3DDevice9 **device)
{
CriticalSection cs(this->device);
TRACE("");
if(!device)
{
return INVALIDCALL();
}
this->device->AddRef();
*device = this->device;
return D3D_OK;
}
long Direct3DSwapChain9::GetPresentParameters(D3DPRESENT_PARAMETERS *presentParameters)
{
CriticalSection cs(device);
TRACE("");
if(!presentParameters)
{
return INVALIDCALL();
}
*presentParameters = this->presentParameters;
return D3D_OK;
}
void Direct3DSwapChain9::reset(D3DPRESENT_PARAMETERS *presentParameters)
{
release();
ASSERT(presentParameters->BackBufferCount <= 3); // Maximum of three back buffers
if(presentParameters->BackBufferCount == 0)
{
presentParameters->BackBufferCount = 1;
}
if(presentParameters->BackBufferFormat == D3DFMT_UNKNOWN)
{
D3DDISPLAYMODE displayMode;
GetDisplayMode(&displayMode);
presentParameters->BackBufferFormat = displayMode.Format;
}
D3DDEVICE_CREATION_PARAMETERS creationParameters;
device->GetCreationParameters(&creationParameters);
HWND windowHandle = presentParameters->hDeviceWindow ? presentParameters->hDeviceWindow : creationParameters.hFocusWindow;
if(presentParameters->Windowed && (presentParameters->BackBufferHeight == 0 || presentParameters->BackBufferWidth == 0))
{
RECT rectangle;
GetClientRect(windowHandle, &rectangle);
presentParameters->BackBufferWidth = rectangle.right - rectangle.left;
presentParameters->BackBufferHeight = rectangle.bottom - rectangle.top;
}
frameBuffer = createFrameBufferWin(windowHandle, presentParameters->BackBufferWidth, presentParameters->BackBufferHeight, presentParameters->Windowed == FALSE, true);
lockable = presentParameters->Flags & D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
backBuffer[0] = 0;
backBuffer[1] = 0;
backBuffer[2] = 0;
for(int i = 0; i < (int)presentParameters->BackBufferCount; i++)
{
backBuffer[i] = new Direct3DSurface9(device, this, presentParameters->BackBufferWidth, presentParameters->BackBufferHeight, presentParameters->BackBufferFormat, D3DPOOL_DEFAULT, presentParameters->MultiSampleType, presentParameters->MultiSampleQuality, lockable, D3DUSAGE_RENDERTARGET);
backBuffer[i]->bind();
}
this->presentParameters = *presentParameters;
}
void Direct3DSwapChain9::release()
{
delete frameBuffer;
frameBuffer = 0;
for(int i = 0; i < 3; i++)
{
if(backBuffer[i])
{
backBuffer[i]->unbind();
backBuffer[i] = 0;
}
}
}
void Direct3DSwapChain9::setGammaRamp(sw::GammaRamp *gammaRamp, bool calibrate)
{
frameBuffer->setGammaRamp(gammaRamp, calibrate);
}
void Direct3DSwapChain9::getGammaRamp(sw::GammaRamp *gammaRamp)
{
frameBuffer->getGammaRamp(gammaRamp);
}
void *Direct3DSwapChain9::lockBackBuffer(int index)
{
return backBuffer[index]->lockInternal(0, 0, 0, sw::LOCK_READWRITE, sw::PUBLIC); // FIXME: External
}
void Direct3DSwapChain9::unlockBackBuffer(int index)
{
backBuffer[index]->unlockInternal(); // FIXME: External
}
}