Skip to content

Commit

Permalink
Add fallback verification method for AV1 filmgrain
Browse files Browse the repository at this point in the history
Implementations are allowed to diverge from the process specified in
the AV1 standard for film grain synthesis, and still be considered a
conformant implementation. The CTS currently performs a bit-exact
check for the process given by the standard, failing implementations
that technically are compliant.

The fallback's method is to create two decoding sessions, one with
film grain applied as usual, and a second with film grain forcibly
disabled. PSNR is computed on the outputs of both sessions, and
checked to be within a reasonable range. This is a non-principled
comparison.

Components: Vulkan

VK-GL-CTS issue: 5016

Affects:

dEQP-VK.video.*

Change-Id: Ib998008002736836612ccde5eeaf15edf3c6560e
  • Loading branch information
charlie-ht authored and lordalcol committed Apr 5, 2024
1 parent b27841a commit 1f4ebee
Show file tree
Hide file tree
Showing 9 changed files with 316 additions and 241 deletions.
34 changes: 26 additions & 8 deletions external/vulkancts/modules/vulkan/video/vktBufferedReader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
*/
#include <fstream>
#include <vector>
#include <memory>
#include <sstream>

#include "deFilePath.hpp"

#include "tcuDefs.hpp"

Expand All @@ -33,23 +37,31 @@ namespace video
class BufferedReader
{
public:
BufferedReader(std::istream& in)
: m_istream(in)
// Open and read from filename
BufferedReader(const std::string& filename)
: m_istream(std::make_unique<std::ifstream>(resourceRelativePath(filename).getPath(), std::ios_base::binary))
{
if (!m_istream.good())
if (!m_istream->good())
{
throw tcu::ResourceError(std::string("failed to open input"));
}
}

// Read from in-memory stream.
BufferedReader(const char* bytes, size_t length)
{
std::string asString(bytes, length);
m_istream.reset(new std::istringstream(asString, std::ios::binary));
}

void read(std::vector<uint8_t>& buffer)
{
m_istream.read(reinterpret_cast<char *>(buffer.data()), buffer.size());
m_istream->read(reinterpret_cast<char *>(buffer.data()), buffer.size());
}

void read(uint8_t* out, size_t n)
{
m_istream.read(reinterpret_cast<char *>(out), n);
m_istream->read(reinterpret_cast<char *>(out), n);
}

void readChecked(uint8_t* out, size_t n, const char* msg)
Expand All @@ -70,11 +82,17 @@ class BufferedReader
return v;
}

bool isError() const { return m_istream.bad() || m_istream.fail(); }
bool isEof() const { return m_istream.eof(); }
bool isError() const { return m_istream->bad() || m_istream->fail(); }
bool isEof() const { return m_istream->eof(); }

private:
std::istream& m_istream;
const de::FilePath resourceRelativePath(const std::string filename) const {
std::vector<std::string> resourcePathComponents = { "vulkan", "video", filename };
de::FilePath resourcePath = de::FilePath::join(resourcePathComponents);
return resourcePath;
}

std::unique_ptr<std::istream> m_istream;
};

} // namespace video
Expand Down
8 changes: 4 additions & 4 deletions external/vulkancts/modules/vulkan/video/vktDemuxer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,21 @@ namespace vkt
namespace video
{

std::unique_ptr<Demuxer> Demuxer::create(Params&& params)
std::shared_ptr<Demuxer> Demuxer::create(Params&& params)
{
switch (params.codecOperation)
{
case vk::VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
case vk::VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:
{
return std::make_unique<H26XAnnexBDemuxer>(std::move(params));
return std::make_shared<H26XAnnexBDemuxer>(std::move(params));
} break;
case vk::VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR:
{
if (params.framing == ElementaryStreamFraming::AV1_ANNEXB)
return std::make_unique<AV1AnnexBDemuxer>(std::move(params));
return std::make_shared<AV1AnnexBDemuxer>(std::move(params));
else if (params.framing == ElementaryStreamFraming::IVF)
return std::make_unique<DuckIVFDemuxer>(std::move(params));
return std::make_shared<DuckIVFDemuxer>(std::move(params));
else
TCU_THROW(InternalError, "unknown elementary stream framing");
} break;
Expand Down
2 changes: 1 addition & 1 deletion external/vulkancts/modules/vulkan/video/vktDemuxer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class Demuxer
ElementaryStreamFraming framing;
};

static std::unique_ptr<Demuxer> create(Params&& params);
static std::shared_ptr<Demuxer> create(Params&& params);

vk::VkVideoCodecOperationFlagBitsKHR codecOperation() const { return m_params.codecOperation; };
ElementaryStreamFraming framing() const { return m_params.framing; }
Expand Down
46 changes: 13 additions & 33 deletions external/vulkancts/modules/vulkan/video/vktVideoBaseDecodeUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,17 +77,16 @@ static constexpr deUint32 H26X_MAX_DPB_SLOTS = 16u;

using VkVideoParser = VkSharedBaseObj<VulkanVideoDecodeParser>;

void createParser(VkVideoCodecOperationFlagBitsKHR codecOperation,
const VkExtensionProperties* extensionProperties,
std::shared_ptr<VideoBaseDecoder> decoder,
VkVideoParser& parser,
ElementaryStreamFraming framing)
void createParser(VkVideoCodecOperationFlagBitsKHR codecOperation,
std::shared_ptr<VideoBaseDecoder> decoder,
VkVideoParser& parser,
ElementaryStreamFraming framing)
{
const VkVideoCapabilitiesKHR* videoCaps = decoder->getVideoCaps();
const VkParserInitDecodeParameters pdParams = {
NV_VULKAN_VIDEO_PARSER_API_VERSION,
dynamic_cast<VkParserVideoDecodeClient*>(decoder.get()),
static_cast<deUint32>(2 * 1024 * 1024), // 2MiB is an arbitrary choice.
static_cast<deUint32>(2 * 1024 * 1024), // 2MiB is an arbitrary choice (and pointless for the CTS)
static_cast<deUint32>(videoCaps->minBitstreamBufferOffsetAlignment),
static_cast<deUint32>(videoCaps->minBitstreamBufferSizeAlignment),
0,
Expand All @@ -96,53 +95,28 @@ void createParser(VkVideoCodecOperationFlagBitsKHR codecOperation,
true,
};

// TODO: Should validate option codec features from the test definition
// - filmgrain (need some capability to check that)
// - intrabc
// - others?

DE_ASSERT(extensionProperties);

switch (codecOperation)
{
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
{
if (strcmp(extensionProperties->extensionName, VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_EXTENSION_NAME) || extensionProperties->specVersion != VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_SPEC_VERSION)
{
tcu::die("The requested decoder h.264 Codec STD version is NOT supported. The supported decoder h.264 Codec STD version is version %d of %s\n",
VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_SPEC_VERSION,
VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_EXTENSION_NAME);
}
VkSharedBaseObj<VulkanH264Decoder> nvVideoH264DecodeParser(new VulkanH264Decoder(codecOperation));
parser = nvVideoH264DecodeParser;
break;
}
case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:
{
if (strcmp(extensionProperties->extensionName, VK_STD_VULKAN_VIDEO_CODEC_H265_DECODE_EXTENSION_NAME) || extensionProperties->specVersion != VK_STD_VULKAN_VIDEO_CODEC_H265_DECODE_SPEC_VERSION)
{
tcu::die("The requested decoder h.265 Codec STD version is NOT supported. The supported decoder h.265 Codec STD version is version %d of %s\n",
VK_STD_VULKAN_VIDEO_CODEC_H265_DECODE_SPEC_VERSION,
VK_STD_VULKAN_VIDEO_CODEC_H265_DECODE_EXTENSION_NAME);
}
VkSharedBaseObj<VulkanH265Decoder> nvVideoH265DecodeParser(new VulkanH265Decoder(codecOperation));
parser = nvVideoH265DecodeParser;
break;
}
case VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR:
{
if (strcmp(extensionProperties->extensionName, VK_STD_VULKAN_VIDEO_CODEC_AV1_DECODE_EXTENSION_NAME) || extensionProperties->specVersion != VK_STD_VULKAN_VIDEO_CODEC_AV1_DECODE_SPEC_VERSION)
{
tcu::die("The requested AV1 Codec STD version is NOT supported. The supported AV1 STD version is version %d of %s\n",
VK_STD_VULKAN_VIDEO_CODEC_AV1_DECODE_SPEC_VERSION,
VK_STD_VULKAN_VIDEO_CODEC_AV1_DECODE_EXTENSION_NAME);
}
VkSharedBaseObj<VulkanAV1Decoder> nvVideoAV1DecodeParser(new VulkanAV1Decoder(codecOperation, framing == ElementaryStreamFraming::AV1_ANNEXB));
parser = nvVideoAV1DecodeParser;
break;
}
default:
TCU_FAIL("Unsupported codec type!");
TCU_FAIL("Unsupported codec type");
}

VK_CHECK(parser->Initialize(&pdParams));
Expand Down Expand Up @@ -415,6 +389,7 @@ VideoBaseDecoder::VideoBaseDecoder(Parameters&& params)
, m_videoFrameBuffer(params.framebuffer)
, m_decodeFramesData(params.context->getDeviceDriver(), params.context->device, params.context->decodeQueueFamilyIdx())
, m_resetPictureParametersFrameTriggerHack(params.pictureParameterUpdateTriggerHack)
, m_forceDisableFilmGrain(params.forceDisableFilmGrain)
, m_queryResultWithStatus(params.queryDecodeStatus)
, m_useInlineQueries(params.useInlineQueries)
, m_resourcesWithoutProfiles(params.resourcesWithoutProfiles)
Expand Down Expand Up @@ -567,7 +542,7 @@ void VideoBaseDecoder::StartVideoSequence (const VkParserDetectedVideoFormat* pV
VK_IMAGE_USAGE_TRANSFER_DST_BIT);
VkImageUsageFlags dpbImageUsage = VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR;

if (dpbAndOutputCoincide() && !pVideoFormat->filmGrainEnabled) {
if (dpbAndOutputCoincide() && (!pVideoFormat->filmGrainEnabled || m_forceDisableFilmGrain)) {
dpbImageUsage = VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR | VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
} else {
m_useSeparateOutputImages = true;
Expand Down Expand Up @@ -1356,6 +1331,11 @@ bool VideoBaseDecoder::DecodePicture (VkParserPictureData* pd,
}
}

if (m_forceDisableFilmGrain)
{
pStd->flags.apply_grain = 0;
}

cachedParameters->pictureParams.filmGrainEnabled = pStd->flags.apply_grain;

pDecodePictureInfo->displayWidth = p->upscaled_width;
Expand Down
33 changes: 15 additions & 18 deletions external/vulkancts/modules/vulkan/video/vktVideoBaseDecodeUtils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,6 @@ using namespace std;
#define NV_FRAME_RATE_NUM(rate) ((rate) >> 14)
#define NV_FRAME_RATE_DEN(rate) ((rate)&0x3fff)

// Set this to 1 to have the decoded YCbCr frames written to the
// filesystem in the YV12 format.
// Check the relevant sections to change the file name and so on...
#define FRAME_DUMP_DEBUG 0

const uint64_t TIMEOUT_100ms = 100 * 1000 * 1000;

// Keeps track of data associated with active internal reference frames
Expand Down Expand Up @@ -825,6 +820,7 @@ class VideoBaseDecoder final : public VkParserVideoDecodeClient
bool alwaysRecreateDPB{};
bool intraOnlyDecoding{};
size_t pictureParameterUpdateTriggerHack{0};
bool forceDisableFilmGrain{false};
VkSharedBaseObj<VulkanVideoFrameBuffer> framebuffer;
};
explicit VideoBaseDecoder(Parameters&& params);
Expand Down Expand Up @@ -971,6 +967,7 @@ class VideoBaseDecoder final : public VkParserVideoDecodeClient
}
}

bool m_forceDisableFilmGrain{false};
bool m_queryResultWithStatus{false};
bool m_useInlineQueries{false};
bool m_resourcesWithoutProfiles{false};
Expand Down Expand Up @@ -1007,32 +1004,32 @@ class VideoBaseDecoder final : public VkParserVideoDecodeClient

using VkVideoParser = VkSharedBaseObj<VulkanVideoDecodeParser>;

void createParser(VkVideoCodecOperationFlagBitsKHR codecOperation,
const VkExtensionProperties* extensionProperties,
// FIXME: sample app interface issues (collapse the interface into CTS eventually)
void createParser(VkVideoCodecOperationFlagBitsKHR codecOperation,
std::shared_ptr<VideoBaseDecoder> decoder,
VkVideoParser& parser,
ElementaryStreamFraming framing);


class FrameProcessor
{
public:
FrameProcessor(std::unique_ptr<Demuxer>&& demuxer, VkVideoParser parser, std::shared_ptr<VideoBaseDecoder> decoder);
FrameProcessor() { }
FrameProcessor(std::shared_ptr<Demuxer>&& demuxer, std::shared_ptr<VideoBaseDecoder> decoder);

void parseNextChunk();
int getNextFrame(DecodedFrame* pFrame);
void bufferFrames(int framesToDecode);
int getBufferedDisplayCount() const {
if (std::shared_ptr<VideoBaseDecoder> decoder = m_decoder.lock())
return decoder->GetVideoFrameBuffer()->GetDisplayedFrameCount();
return 0;
return m_decoder->GetVideoFrameBuffer()->GetDisplayedFrameCount();
}

private:
VkVideoParser m_parser;
std::weak_ptr<VideoBaseDecoder> m_decoder;
std::unique_ptr<Demuxer> m_demuxer;
bool m_eos{false};
void decodeFrameOutOfOrder(int framesToCheck) {
bufferFrames(framesToCheck);
m_decoder->decodeFramesOutOfOrder();
}
std::shared_ptr<VideoBaseDecoder> m_decoder{};
VkVideoParser m_parser{};
std::shared_ptr<Demuxer> m_demuxer{};
bool m_eos{false};
};

} // namespace video
Expand Down
Loading

0 comments on commit 1f4ebee

Please sign in to comment.