scummvm/engines/ags/tests/test_gfx.cpp
2023-08-29 21:51:36 +02:00

277 lines
13 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "ags/shared/debugging/out.h"
#include "common/scummsys.h"
#include "common/debug.h"
#include "ags/shared/core/platform.h"
#include "ags/shared/gfx/gfx_def.h"
#include "common/scummsys.h"
#include "ags/lib/allegro/color.h"
#include "ags/shared/gfx/bitmap.h"
#include "ags/shared/gfx/image.h"
#include "ags/lib/allegro/surface.h"
#include "ags/shared/debugging/debug_manager.h"
#include "ags/globals.h"
#include "graphics/managed_surface.h"
#include "graphics/pixelformat.h"
namespace AGS3 {
namespace GfxDef = AGS::Shared::GfxDef;
using namespace AGS::Shared;
void Test_GfxSpeed(bool enableSimd, size_t blenderModeStart, size_t blenderModeEnd) {
uint oldSimdFlags = _G(simd_flags);
if (!enableSimd) _G(simd_flags) = AGS3::Globals::SIMD_NONE;
if (enableSimd) debug("SIMD optimizations: true\n");
else debug("SIMD optmizations: false\n");
Bitmap *benchgfx32 = BitmapHelper::CreateBitmap(100, 100, 32);
Bitmap *benchgfx16 = BitmapHelper::CreateBitmapCopy(benchgfx32, 16);
Bitmap *benchgfx8 = BitmapHelper::CreateBitmap(100, 100, 8);
Bitmap *dest32 = BitmapHelper::CreateBitmap(100, 100, 32);
Bitmap *dest16 = BitmapHelper::CreateBitmap(100, 100, 16);
Bitmap *dest8 = BitmapHelper::CreateBitmap(100, 100, 8);
int benchRuns[] = {1000, 10000, 100000};
int blenderModes[] = {kRgbToRgbBlender, kSourceAlphaBlender, kArgbToArgbBlender, kOpaqueBlenderMode, kTintLightBlenderMode};
//const char *modeNames[] = {"RGB to RGB", "Source Alpha", "ARGB to ARGB", "Opaque", "Tint with Light"};
Bitmap *destinations[] = {dest32, dest16, dest8};
Bitmap *graphics[] = {benchgfx32, benchgfx16, benchgfx8};
uint64 time = 0, numIters = 0, timeNotStretched = 0, numItersNotStretched = 0, timeCommon = 0, numItersCommon = 0;
//int bpps[] = {32, 16, 8};
if (blenderModeEnd >= sizeof(blenderModes) / sizeof(blenderModes[0])) blenderModeEnd = (sizeof(blenderModes) / sizeof(blenderModes[0])) - 1;
for (int dest = 0; dest < 3; dest++) {
for (int gfx = 0; gfx < 3; gfx++) {
if (dest == 2 && gfx != 2) continue;
for (size_t mode = blenderModeStart; mode <= blenderModeEnd; mode++) {
for (int runs = 0; (size_t)runs < sizeof(benchRuns)/sizeof(int); runs++) {
uint32 start, end;
_G(_blender_mode) = (AGS3::BlenderMode)blenderModes[mode];
//if (runs == 2) debug("Dest: %d bpp, Gfx: %d bpp, Blender: %s, Stretched: false, Iters: %d\n", bpps[dest], bpps[gfx], modeNames[mode], benchRuns[runs]);
start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < benchRuns[runs]; i++)
destinations[dest]->Blit(graphics[gfx], 0, 0, kBitmap_Transparency);
end = std::chrono::high_resolution_clock::now();
timeNotStretched += end - start;
numItersNotStretched += benchRuns[runs];
if (mode == kArgbToArgbBlender || mode == kRgbToRgbBlender || mode == kRgbToArgbBlender || mode == kArgbToRgbBlender) {
timeCommon += end - start;
numItersCommon += benchRuns[runs];
}
time += end - start;
//if (runs == 2) debug("exec time (mills): %u\n\n", end - start);
//if (runs == 2) debug("Dest: %d bpp, Gfx: %d bpp, Blender: %s, Stretched: true, Iters: %d\n", bpps[dest], bpps[gfx], modeNames[mode], benchRuns[runs]);
start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < benchRuns[runs]; i++)
destinations[dest]->StretchBlt(graphics[gfx], Rect(0, 0, 99, 99), kBitmap_Transparency);
end = std::chrono::high_resolution_clock::now();
time += end - start;
numIters += benchRuns[runs] * 2;
//if (runs == 2) debug("exec time (mills): %u\n\n", end - start);
}
}
}
}
debug("Over all blender modes, pixel formats, and stretching sizes (%f) avg millis per call.", (double)time / (double)numIters);
debug("Over all blender modes, pixel formats, but only unstretched (%f) avg millis per call.", (double)timeNotStretched / (double)numItersNotStretched);
debug("Over most common blender modes, all pixel formats, but only unstretched (%f) avg millis per call.", (double)timeCommon / (double)numItersCommon);
delete benchgfx32;
delete benchgfx16;
delete benchgfx8;
delete dest32;
delete dest16;
delete dest8;
if (!enableSimd) _G(simd_flags) = oldSimdFlags;
}
void Test_BlenderModes() {
constexpr int depth = 2;
Graphics::ManagedSurface owner(16, 16, Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24));
BITMAP dummy(&owner);
Graphics::ManagedSurface owner16(16, 16, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
BITMAP dummy16(&owner16);
Graphics::ManagedSurface ownerDest(16, 16, Graphics::PixelFormat(4, 8, 8, 8, 8, 16, 8, 0, 24));
BITMAP dummyDest(&ownerDest);
Graphics::ManagedSurface ownerDest16(16, 16, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0));
BITMAP dummyDest16(&ownerDest16);
for (int blenderMode = (int)kSourceAlphaBlender; blenderMode <= (int)kTintLightBlenderMode; blenderMode++) {
_G(_blender_mode) = (BlenderMode)blenderMode;
for (int srcR = 0; srcR < 255; srcR += (1 << (8 - depth))) {
for (int srcG = 0; srcG < 255; srcG += (1 << (8 - depth))) {
for (int srcB = 0; srcB < 255; srcB += (1 << (8 - depth))) {
for (int srcA = 0; srcA < 255; srcA += (1 << (8 - depth))) {
for (int destR = 0; destR < 255; destR += (1 << (8 - depth))) {
for (int destG = 0; destG < 255; destG += (1 << (8 - depth))) {
for (int destB = 0; destB < 255; destB += (1 << (8 - depth))) {
for (int destA = 0; destA < 255; destA += (1 << (8 - depth))) {
for (uint32 alpha = 0; alpha < 255; alpha += (1 << (8 - depth))) {
// First run the non-vectorized version of the code
uint32 controlCol = 0, simdCol = 0;
uint16 control2bppCol = 0, simd2bppCol = 0;
uint8 a, r, g, b, a16, r16, g16, b16;
a = r = g = b = a16 = r16 = g16 = b16 = 0;
auto printInfo = [&]() {
debug("src argb: %d, %d, %d, %d dest argb: %d, %d, %d, %d a: %d", srcA, srcR, srcG, srcB, destA, destR, destG, destB, alpha);
switch ((BlenderMode)blenderMode) {
case kSourceAlphaBlender: debug("blenderMode: kSourceAlphaBlender"); break;
case kArgbToArgbBlender: debug("blenderMode: kArgbToArgbBlender"); break;
case kArgbToRgbBlender: debug("blenderMode: kArgbToRgbBlender"); break;
case kRgbToArgbBlender: debug("blenderMode: kRgbToArgbBlender"); break;
case kRgbToRgbBlender: debug("blenderMode: kRgbToRgbBlender"); break;
case kAlphaPreservedBlenderMode: debug("blenderMode: kAlphaPreservedBlenderMode"); break;
case kOpaqueBlenderMode: debug("blenderMode: kOpaqueBlenderMode"); break;
case kAdditiveBlenderMode: debug("blenderMode: kAdditiveBlenderMode"); break;
case kTintBlenderMode: debug("blenderMode: kTintBlenderMode"); break;
case kTintLightBlenderMode: debug("blenderMode: kTintLightBlenderMode"); break;
}
debug("controlCol %x argb: %d, %d, %d, %d", controlCol, a, r, g, b);
debug("simdCol %x argb: %d, %d, %d, %d", simdCol, (simdCol >> 24), ((simdCol >> 16) & 0xff), ((simdCol >> 8) & 0xff), (simdCol & 0xff));
debug("control2bppCol %x rgb: %d, %d, %d", control2bppCol, r16, g16, b16);
debug("simd2bppCol %x rgb: %d, %d, %d", simd2bppCol, (simd2bppCol >> 11), ((simd2bppCol >> 5) & 0x3f), (simd2bppCol & 0x1f));
};
uint oldSimdFlags = _G(simd_flags);
_G(simd_flags) = AGS3::Globals::SIMD_NONE;
*(uint32 *)dummy.getBasePtr(0, 0) = dummy.format.ARGBToColor(srcA, srcR, srcG, srcB);
*(uint32 *)dummyDest.getBasePtr(0, 0) = dummyDest.format.ARGBToColor(destA, destR, destG, destB);
dummyDest.draw(&dummy, Common::Rect(16, 16), 0, 0, false, false, false, alpha);
controlCol = dummyDest.getpixel(0, 0);
dummyDest.format.colorToARGB(dummyDest.getpixel(0, 0), a, r, g, b);
*(uint16 *)dummy16.getBasePtr(0, 0) = dummy16.format.ARGBToColor(srcA, srcR, srcG, srcB);
*(uint16 *)dummyDest16.getBasePtr(0, 0) = dummyDest16.format.ARGBToColor(destA, destR, destG, destB);
dummyDest16.draw(&dummy16, Common::Rect(16, 16), 0, 0, false, false, false, alpha);
control2bppCol = dummyDest16.getpixel(0, 0);
dummyDest16.format.colorToARGB(dummyDest16.getpixel(0, 0), a16, r16, g16, b16);
a16 >>= 3; r16 >>= 3; g16 >>= 2; b16 >>= 3;
_G(simd_flags) = oldSimdFlags;
*(uint32 *)dummy.getBasePtr(0, 0) = dummy.format.ARGBToColor(srcA, srcR, srcG, srcB);
*(uint32 *)dummyDest.getBasePtr(0, 0) = dummyDest.format.ARGBToColor(destA, destR, destG, destB);
dummyDest.draw(&dummy, Common::Rect(16, 16), 0, 0, false, false, false, alpha);
simdCol = dummyDest.getpixel(0, 0);
*(uint16 *)dummy16.getBasePtr(0, 0) = dummy16.format.ARGBToColor(srcA, srcR, srcG, srcB);
*(uint16 *)dummyDest16.getBasePtr(0, 0) = dummyDest16.format.ARGBToColor(destA, destR, destG, destB);
dummyDest16.draw(&dummy16, Common::Rect(16, 16), 0, 0, false, false, false, alpha);
simd2bppCol = dummyDest16.getpixel(0, 0);
int tolerance, tolerance16;
switch ((BlenderMode)blenderMode) {
// These need to be IDENTICAL for lamplight city to work
// It would be nice to get tolerance16 down to 0 though...
case kRgbToRgbBlender:
case kArgbToRgbBlender:
case kSourceAlphaBlender:
case kAlphaPreservedBlenderMode:
tolerance = 0;
tolerance16 = 1;
break;
// These can be 1 or 2 off, as long as they look the same
case kArgbToArgbBlender:
case kRgbToArgbBlender:
case kOpaqueBlenderMode:
case kAdditiveBlenderMode:
tolerance = 1;
tolerance16 = 1;
break;
case kTintBlenderMode:
case kTintLightBlenderMode:
tolerance = 2;
tolerance16 = 1;
break;
default:
tolerance = 0;
}
if (std::abs((int)a - (int)(simdCol >> 24)) > tolerance) {
printInfo();
assert(false && "a is over the tolerance");
}
if (std::abs((int)r - (int)((simdCol >> 16) & 0xff)) > tolerance) {
printInfo();
assert(false && "r is over the tolerance");
}
if (std::abs((int)g - (int)((simdCol >> 8) & 0xff)) > tolerance) {
printInfo();
assert(false && "g is over the tolerance");
}
if (std::abs((int)b - (int)(simdCol & 0xff)) > tolerance) {
printInfo();
assert(false && "b is over the tolerance");
}
if (std::abs((int)b16 - (int)(simd2bppCol & 0x1f)) > tolerance16) {
printInfo();
assert(false && "b16 is over the tolerance");
}
if (std::abs((int)g16 - (int)((simd2bppCol >> 5) & 0x3f)) > tolerance16) {
printInfo();
assert(false && "g16 is over the tolerance");
}
if (std::abs((int)r16 - (int)(simd2bppCol >> 11)) > tolerance16) {
printInfo();
assert(false && "r16 is over the tolerance");
}
}
}
}
}
}
}
}
}
}
}
}
void Test_GfxTransparency() {
// Test that every transparency which is a multiple of 10 is converted
// forth and back without loosing precision
const size_t arr_sz = 11;
const int trans100[arr_sz] = { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 };
int trans255[arr_sz] = { 0 };
int trans100_back[arr_sz] = { 0 };
for (size_t i = 0; i < arr_sz; ++i) {
trans255[i] = GfxDef::Trans100ToLegacyTrans255(trans100[i]);
trans100_back[i] = GfxDef::LegacyTrans255ToTrans100(trans255[i]);
assert(trans100[i] == trans100_back[i]);
}
}
void Test_Gfx() {
Test_GfxTransparency();
#if (defined(SCUMMVM_AVX2) || defined(SCUMMVM_SSE2) || defined(SCUMMVM_NEON)) && defined(SLOW_TESTS)
Test_BlenderModes();
// This could take a LONG time
Test_GfxSpeed(true, kSourceAlphaBlender, kTintLightBlenderMode);
Test_GfxSpeed(false, kSourceAlphaBlender, kTintLightBlenderMode);
#endif
}
} // namespace AGS3