mirror of
https://github.com/gnif/LookingGlass.git
synced 2026-11-13 15:27:21 +00:00
[client/idd] network: add GPU video transport
This commit is contained in:
@@ -15,6 +15,9 @@ set(TRANSPORT_NETWORK_SOURCES
|
||||
known_hosts.c
|
||||
network.c
|
||||
recovery.c
|
||||
video.c
|
||||
video/decoder_egl.c
|
||||
video/pyrowave.c
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
@@ -40,6 +43,7 @@ target_include_directories(transport_Network PRIVATE
|
||||
|
||||
target_link_libraries(transport_Network PRIVATE
|
||||
lg_common
|
||||
lg_egl_provider
|
||||
msquic::msquic
|
||||
)
|
||||
|
||||
|
||||
@@ -1071,8 +1071,7 @@ static void networkConnectionSetSessionFeatures(NetworkConnection * this)
|
||||
if (services & (UINT16_C(1) << LG_NET_SERVICE_CONTROL))
|
||||
this->session.features |=
|
||||
LG_TRANSPORT_FEATURE_SET_CURSOR_POS |
|
||||
LG_TRANSPORT_FEATURE_WINDOW_SIZE |
|
||||
LG_TRANSPORT_FEATURE_FRAME_SCHEDULE;
|
||||
LG_TRANSPORT_FEATURE_WINDOW_SIZE;
|
||||
}
|
||||
|
||||
static bool networkConnectionHandleAuthChallenge(NetworkConnection * this,
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "input.h"
|
||||
#include "known_hosts.h"
|
||||
#include "recovery.h"
|
||||
#include "video.h"
|
||||
|
||||
#include "app.h"
|
||||
#include "interface/renderer.h"
|
||||
@@ -56,6 +57,9 @@
|
||||
(LG_NET_CLIPBOARD_CHUNK_HEADER_WIRE_SIZE + NETWORK_TRANSFER_CHUNK)
|
||||
#define NETWORK_FILE_MAX_PAYLOAD \
|
||||
(LG_NET_FILE_CHUNK_HEADER_WIRE_SIZE + NETWORK_TRANSFER_CHUNK)
|
||||
#define NETWORK_VIDEO_MAX_PAYLOAD \
|
||||
(LG_NET_VIDEO_FRAGMENT_HEADER_WIRE_SIZE + \
|
||||
LG_NET_VIDEO_MAX_FRAGMENT_LENGTH)
|
||||
|
||||
static const LGNetCapability NETWORK_CAPABILITIES[] =
|
||||
{
|
||||
@@ -94,6 +98,19 @@ static const LGNetCapability NETWORK_CAPABILITIES[] =
|
||||
.maxBidirectionalStreams = 0,
|
||||
.maxUnidirectionalStreams = 0,
|
||||
},
|
||||
{
|
||||
.service = LG_NET_SERVICE_VIDEO,
|
||||
.versionMin =
|
||||
LG_NET_VIDEO_BLOCK_FRAGMENTATION_INTRODUCED_SERVICE_VERSION,
|
||||
.versionMax = LG_NET_VIDEO_VERSION_CURRENT,
|
||||
.flags = LG_NET_CAPABILITY_RELIABLE |
|
||||
LG_NET_CAPABILITY_DATAGRAM | LG_NET_CAPABILITY_BULK,
|
||||
.features = 0,
|
||||
.maxPayloadLength = NETWORK_VIDEO_MAX_PAYLOAD,
|
||||
.maxDatagramPayload = NETWORK_MAX_DATAGRAM,
|
||||
.maxBidirectionalStreams = 0,
|
||||
.maxUnidirectionalStreams = 1,
|
||||
},
|
||||
{
|
||||
.service = LG_NET_SERVICE_AUDIO,
|
||||
.versionMin =
|
||||
@@ -160,8 +177,8 @@ struct LG_Transport
|
||||
NetworkCursor * cursor;
|
||||
NetworkInput * input;
|
||||
NetworkRecovery * recovery;
|
||||
NetworkVideo * video;
|
||||
NetworkKnownHosts knownHosts;
|
||||
LGEvent * frameEvent;
|
||||
LGEvent * promptIdle;
|
||||
|
||||
LG_Lock callbackLock;
|
||||
@@ -172,7 +189,6 @@ struct LG_Transport
|
||||
LG_VideoStatusFn videoStatusFn;
|
||||
void * videoStatusOpaque;
|
||||
|
||||
bool rendererAttached;
|
||||
bool promptCallbackActive;
|
||||
struct NetworkTrustPrompt prompt;
|
||||
};
|
||||
@@ -340,6 +356,17 @@ static void networkNotifyVideoStatus(LG_Transport * this)
|
||||
LG_UNLOCK(this->callbackLock);
|
||||
}
|
||||
|
||||
static void networkVideoStatus(void * opaque,
|
||||
const LG_VideoComponentStatus * status)
|
||||
{
|
||||
LG_Transport * this = opaque;
|
||||
|
||||
LG_LOCK(this->stateLock);
|
||||
this->videoStatus.frame = *status;
|
||||
LG_UNLOCK(this->stateLock);
|
||||
networkNotifyVideoStatus(this);
|
||||
}
|
||||
|
||||
static uint64_t networkNextEpoch(uint64_t epoch)
|
||||
{
|
||||
++epoch;
|
||||
@@ -375,18 +402,17 @@ static void networkConnectionState(void * opaque, bool connected,
|
||||
networkClipboardConnectionState(this->clipboard, connected);
|
||||
networkControlConnectionState(this->control, connected);
|
||||
networkRecoveryConnectionState(this->recovery, connected);
|
||||
networkVideoConnectionState(this->video, connected);
|
||||
const bool cursorAvailable =
|
||||
networkCursorConnectionState(this->cursor, connected);
|
||||
|
||||
LG_LOCK(this->stateLock);
|
||||
networkSetUnavailable(&this->videoStatus.frame, status);
|
||||
if (cursorAvailable)
|
||||
networkSetAvailable(&this->videoStatus.pointer);
|
||||
else
|
||||
networkSetUnavailable(&this->videoStatus.pointer, status);
|
||||
LG_UNLOCK(this->stateLock);
|
||||
|
||||
lgSignalEvent(this->frameEvent);
|
||||
networkNotifyVideoStatus(this);
|
||||
}
|
||||
|
||||
@@ -416,6 +442,10 @@ static void networkPacket(void * opaque, const LGNetPacketView * packet,
|
||||
(datagram ? networkAudioDatagram(this->audio, packet) :
|
||||
networkAudioPacket(this->audio, packet));
|
||||
break;
|
||||
case LG_NET_SERVICE_VIDEO:
|
||||
valid = datagram ? networkVideoDatagram(this->video, packet) :
|
||||
networkVideoPacket(this->video, packet);
|
||||
break;
|
||||
case LG_NET_SERVICE_CLIPBOARD:
|
||||
case LG_NET_SERVICE_FILE:
|
||||
valid = delivery != NETWORK_PACKET_DELIVERY_DATAGRAM &&
|
||||
@@ -451,7 +481,9 @@ static void networkSendReady(void * opaque, LGNetService service,
|
||||
if ((service == LG_NET_SERVICE_CLIPBOARD ||
|
||||
service == LG_NET_SERVICE_FILE) && !datagram)
|
||||
networkClipboardSendReady(this->clipboard, service, delivery);
|
||||
lgSignalEvent(this->frameEvent);
|
||||
if (service == LG_NET_SERVICE_VIDEO &&
|
||||
delivery != NETWORK_PACKET_DELIVERY_BULK)
|
||||
networkVideoSendReady(this->video, datagram);
|
||||
}
|
||||
|
||||
static bool networkCreate(LG_Transport ** result)
|
||||
@@ -474,10 +506,8 @@ static bool networkCreate(LG_Transport ** result)
|
||||
if (!networkKnownHostsInit(&this->knownHosts))
|
||||
goto fail;
|
||||
|
||||
this->frameEvent = lgCreateEvent(true, 0);
|
||||
this->promptIdle = lgCreateEvent(false, 0);
|
||||
if (!this->frameEvent || !this->promptIdle ||
|
||||
!lgSignalEvent(this->promptIdle))
|
||||
if (!this->promptIdle || !lgSignalEvent(this->promptIdle))
|
||||
goto fail;
|
||||
|
||||
const char * address = option_get_string("network", "address");
|
||||
@@ -516,13 +546,16 @@ static bool networkCreate(LG_Transport ** result)
|
||||
!networkControlCreate(&this->control, this->connection) ||
|
||||
!networkCursorCreate(&this->cursor, this->connection) ||
|
||||
!networkInputCreate(&this->input, this->connection) ||
|
||||
!networkRecoveryCreate(&this->recovery, this->connection))
|
||||
!networkRecoveryCreate(&this->recovery, this->connection) ||
|
||||
!networkVideoCreate(&this->video, this->connection))
|
||||
goto fail;
|
||||
networkVideoSetStatusListener(this->video, networkVideoStatus, this);
|
||||
|
||||
*result = this;
|
||||
return true;
|
||||
|
||||
fail:
|
||||
networkVideoDestroy(&this->video);
|
||||
networkRecoveryDestroy(&this->recovery);
|
||||
networkInputDestroy(&this->input);
|
||||
networkCursorDestroy(&this->cursor);
|
||||
@@ -530,8 +563,6 @@ fail:
|
||||
networkClipboardDestroy(&this->clipboard);
|
||||
networkAudioDestroy(&this->audio);
|
||||
networkConnectionDestroy(&this->connection);
|
||||
if (this->frameEvent)
|
||||
lgFreeEvent(this->frameEvent);
|
||||
if (this->promptIdle)
|
||||
lgFreeEvent(this->promptIdle);
|
||||
networkKnownHostsFree(&this->knownHosts);
|
||||
@@ -560,13 +591,13 @@ static void networkDestroy(LG_Transport ** transport)
|
||||
LG_UNLOCK(this->callbackLock);
|
||||
|
||||
networkRecoveryDestroy(&this->recovery);
|
||||
networkVideoDestroy(&this->video);
|
||||
networkInputDestroy(&this->input);
|
||||
networkCursorDestroy(&this->cursor);
|
||||
networkControlDestroy(&this->control);
|
||||
networkClipboardDestroy(&this->clipboard);
|
||||
networkAudioDestroy(&this->audio);
|
||||
networkConnectionDestroy(&this->connection);
|
||||
lgFreeEvent(this->frameEvent);
|
||||
lgFreeEvent(this->promptIdle);
|
||||
networkKnownHostsFree(&this->knownHosts);
|
||||
LG_LOCK_FREE(this->promptLock);
|
||||
@@ -597,9 +628,9 @@ static void networkDisconnect(LG_Transport * this)
|
||||
networkClipboardConnectionState(this->clipboard, false);
|
||||
networkControlConnectionState(this->control, false);
|
||||
networkRecoveryConnectionState(this->recovery, false);
|
||||
networkVideoConnectionState(this->video, false);
|
||||
networkCursorConnectionState(this->cursor, false);
|
||||
networkConnectionDisconnect(this->connection);
|
||||
lgSignalEvent(this->frameEvent);
|
||||
}
|
||||
|
||||
static bool networkSessionValid(LG_Transport * this)
|
||||
@@ -633,67 +664,40 @@ static bool networkSupportsDMA(LG_Transport * this)
|
||||
static bool networkAttachRenderer(LG_Transport * this,
|
||||
const LG_RendererInterop * interop)
|
||||
{
|
||||
const size_t requiredSize = offsetof(LG_RendererInterop,
|
||||
egl.sharedTextureImport) + sizeof(interop->egl.sharedTextureImport);
|
||||
if (!interop ||
|
||||
interop->version < LG_RENDERER_INTEROP_EGL_TEXTURE_VERSION ||
|
||||
interop->size < requiredSize ||
|
||||
interop->type != LG_RENDERER_INTEROP_EGL ||
|
||||
!interop->egl.display || !interop->egl.config ||
|
||||
!interop->egl.shareContext || !interop->egl.sharedTextureImport)
|
||||
{
|
||||
DEBUG_ERROR("The network transport requires EGL shared textures");
|
||||
return false;
|
||||
}
|
||||
|
||||
this->rendererAttached = true;
|
||||
return true;
|
||||
return networkVideoAttachRenderer(this->video, interop);
|
||||
}
|
||||
|
||||
static void networkDetachRenderer(LG_Transport * this)
|
||||
{
|
||||
this->rendererAttached = false;
|
||||
networkVideoDetachRenderer(this->video);
|
||||
}
|
||||
|
||||
static LG_TransportStatus networkNextFrame(LG_Transport * this, bool useDMA,
|
||||
LG_TransportFrame * frame)
|
||||
{
|
||||
(void)useDMA;
|
||||
(void)frame;
|
||||
|
||||
if (!this->rendererAttached)
|
||||
return LG_TRANSPORT_ERROR;
|
||||
if (!networkSessionValid(this))
|
||||
return LG_TRANSPORT_DISCONNECTED;
|
||||
|
||||
lgWaitEvent(this->frameEvent, 100);
|
||||
return networkSessionValid(this) ?
|
||||
LG_TRANSPORT_UNAVAILABLE : LG_TRANSPORT_DISCONNECTED;
|
||||
return networkVideoNextFrame(this->video, useDMA, frame);
|
||||
}
|
||||
|
||||
static void networkGetFrameTiming(LG_Transport * this,
|
||||
const LG_TransportFrame * frame, LG_TransportFrameTiming * timing)
|
||||
{
|
||||
(void)this;
|
||||
(void)frame;
|
||||
memset(timing, 0, sizeof(*timing));
|
||||
networkVideoGetFrameTiming(this->video, frame, timing);
|
||||
}
|
||||
|
||||
static void networkReleaseFrame(LG_Transport * this,
|
||||
LG_TransportFrame * frame)
|
||||
{
|
||||
(void)this;
|
||||
(void)frame;
|
||||
networkVideoReleaseFrame(this->video, frame);
|
||||
}
|
||||
|
||||
static void networkCancelFrameWait(LG_Transport * this)
|
||||
{
|
||||
lgSignalEvent(this->frameEvent);
|
||||
networkVideoCancelFrameWait(this->video);
|
||||
}
|
||||
|
||||
static void networkStopFrame(LG_Transport * this)
|
||||
{
|
||||
lgSignalEvent(this->frameEvent);
|
||||
networkVideoStopFrame(this->video);
|
||||
}
|
||||
|
||||
static LG_TransportStatus networkNextPointer(LG_Transport * this,
|
||||
|
||||
1671
client/transports/Network/video.c
Normal file
1671
client/transports/Network/video.c
Normal file
File diff suppressed because it is too large
Load Diff
65
client/transports/Network/video.h
Normal file
65
client/transports/Network/video.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* 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 2 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, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "connection.h"
|
||||
|
||||
#include "interface/transport.h"
|
||||
|
||||
#include <LGProtocol/NetworkProtocol.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct NetworkVideo NetworkVideo;
|
||||
|
||||
typedef void (*NetworkVideoStatusFn)(void * opaque,
|
||||
const LG_VideoComponentStatus * status);
|
||||
|
||||
bool networkVideoCreate(NetworkVideo ** video,
|
||||
NetworkConnection * connection);
|
||||
void networkVideoDestroy(NetworkVideo ** video);
|
||||
|
||||
void networkVideoConnectionState(NetworkVideo * video, bool connected);
|
||||
void networkVideoProcess(NetworkVideo * video);
|
||||
void networkVideoSendReady(NetworkVideo * video, bool datagram);
|
||||
|
||||
bool networkVideoPacket(NetworkVideo * video,
|
||||
const LGNetPacketView * packet);
|
||||
bool networkVideoDatagram(NetworkVideo * video,
|
||||
const LGNetPacketView * packet);
|
||||
|
||||
void networkVideoSetStatusListener(NetworkVideo * video,
|
||||
NetworkVideoStatusFn callback, void * opaque);
|
||||
LG_VideoComponentStatus networkVideoGetStatus(NetworkVideo * video);
|
||||
|
||||
bool networkVideoAttachRenderer(NetworkVideo * video,
|
||||
const LG_RendererInterop * interop);
|
||||
void networkVideoDetachRenderer(NetworkVideo * video);
|
||||
|
||||
LG_TransportStatus networkVideoNextFrame(NetworkVideo * video, bool useDMA,
|
||||
LG_TransportFrame * frame);
|
||||
void networkVideoGetFrameTiming(NetworkVideo * video,
|
||||
const LG_TransportFrame * frame, LG_TransportFrameTiming * timing);
|
||||
void networkVideoReleaseFrame(NetworkVideo * video,
|
||||
LG_TransportFrame * frame);
|
||||
void networkVideoCancelFrameWait(NetworkVideo * video);
|
||||
void networkVideoStopFrame(NetworkVideo * video);
|
||||
67
client/transports/Network/video/decoder.h
Normal file
67
client/transports/Network/video/decoder.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* 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 2 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, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "interface/external_frame.h"
|
||||
#include "interface/renderer.h"
|
||||
#include "interface/transport.h"
|
||||
|
||||
#include <LGProtocol/NetworkServices.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct NetworkVideoDecoder NetworkVideoDecoder;
|
||||
|
||||
typedef enum NetworkVideoDecodeResult
|
||||
{
|
||||
NETWORK_VIDEO_DECODE_OK,
|
||||
NETWORK_VIDEO_DECODE_UNSUPPORTED,
|
||||
NETWORK_VIDEO_DECODE_CORRUPT,
|
||||
NETWORK_VIDEO_DECODE_ERROR,
|
||||
}
|
||||
NetworkVideoDecodeResult;
|
||||
|
||||
typedef struct NetworkVideoDecodedFrame
|
||||
{
|
||||
LG_ExternalFrame * externalFrame;
|
||||
LG_TransportFrameFormat format;
|
||||
LG_TransportFrameTiming timing;
|
||||
uint64_t decodeTimeNs;
|
||||
}
|
||||
NetworkVideoDecodedFrame;
|
||||
|
||||
bool networkVideoDecoderCreate(NetworkVideoDecoder ** decoder);
|
||||
void networkVideoDecoderDestroy(NetworkVideoDecoder ** decoder);
|
||||
|
||||
bool networkVideoDecoderAttach(NetworkVideoDecoder * decoder,
|
||||
const LG_RendererInterop * interop);
|
||||
void networkVideoDecoderDetach(NetworkVideoDecoder * decoder);
|
||||
void networkVideoDecoderReset(NetworkVideoDecoder * decoder);
|
||||
|
||||
NetworkVideoDecodeResult networkVideoDecoderConfigure(
|
||||
NetworkVideoDecoder * decoder, const LGNetVideoStreamConfig * config);
|
||||
NetworkVideoDecodeResult networkVideoDecoderDecode(
|
||||
NetworkVideoDecoder * decoder, const LGNetVideoStreamConfig * config,
|
||||
const void * data, size_t size, NetworkVideoDecodedFrame * frame);
|
||||
|
||||
void networkVideoDecoderRelease(NetworkVideoDecodedFrame * frame);
|
||||
1081
client/transports/Network/video/decoder_egl.c
Normal file
1081
client/transports/Network/video/decoder_egl.c
Normal file
File diff suppressed because it is too large
Load Diff
324
client/transports/Network/video/pyrowave.c
Normal file
324
client/transports/Network/video/pyrowave.c
Normal file
@@ -0,0 +1,324 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* 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 2 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, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* The packet layout and block ordering validated here are adapted from the
|
||||
* PyroWave bitstream specification and reference parser at commit
|
||||
* 89f7e47d4abbf650c91fae766728af866c5e32a0.
|
||||
*
|
||||
* Copyright (c) 2025 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "pyrowave.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define NETWORK_PYROWAVE_HEADER_SIZE 8U
|
||||
#define NETWORK_PYROWAVE_DECOMPOSITION_LEVELS 5U
|
||||
#define NETWORK_PYROWAVE_ALIGNMENT \
|
||||
(1U << NETWORK_PYROWAVE_DECOMPOSITION_LEVELS)
|
||||
#define NETWORK_PYROWAVE_MINIMUM_SIZE \
|
||||
(4U << NETWORK_PYROWAVE_DECOMPOSITION_LEVELS)
|
||||
#define NETWORK_PYROWAVE_SEQUENCE_MASK 7U
|
||||
|
||||
static uint32_t networkPyroWaveReadU32(const uint8_t * data)
|
||||
{
|
||||
return (uint32_t)data[0] |
|
||||
(uint32_t)data[1] << 8 |
|
||||
(uint32_t)data[2] << 16 |
|
||||
(uint32_t)data[3] << 24;
|
||||
}
|
||||
|
||||
static uint16_t networkPyroWaveReadU16(const uint8_t * data)
|
||||
{
|
||||
return (uint16_t)((uint16_t)data[0] | (uint16_t)data[1] << 8);
|
||||
}
|
||||
|
||||
static unsigned networkPyroWaveBitCount(uint32_t value)
|
||||
{
|
||||
unsigned count = 0;
|
||||
while (value)
|
||||
{
|
||||
count += value & 1U;
|
||||
value >>= 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static bool networkPyroWavePacketValid(
|
||||
const uint8_t * packet, size_t packetSize, uint16_t ballot)
|
||||
{
|
||||
const unsigned groups = networkPyroWaveBitCount(ballot);
|
||||
const size_t controlBase = NETWORK_PYROWAVE_HEADER_SIZE;
|
||||
const size_t scaleBase = controlBase + groups * sizeof(uint16_t);
|
||||
const size_t planeBase = scaleBase + groups;
|
||||
if (planeBase > packetSize)
|
||||
return false;
|
||||
|
||||
size_t planeOffset = planeBase;
|
||||
unsigned signs = 0;
|
||||
for (unsigned group = 0; group < 16U; ++group)
|
||||
{
|
||||
if (!(ballot & (UINT32_C(1) << group)))
|
||||
continue;
|
||||
|
||||
const unsigned compact = networkPyroWaveBitCount(
|
||||
ballot & ((UINT32_C(1) << group) - 1U));
|
||||
const uint16_t control = networkPyroWaveReadU16(
|
||||
packet + controlBase + compact * sizeof(uint16_t));
|
||||
const uint8_t qBits = packet[scaleBase + compact] & 15U;
|
||||
|
||||
for (unsigned subblock = 0; subblock < 8U; ++subblock)
|
||||
{
|
||||
const unsigned planes = qBits +
|
||||
((control >> (2U * subblock)) & 3U);
|
||||
if (planes > packetSize - planeOffset)
|
||||
return false;
|
||||
|
||||
uint8_t nonzero = 0;
|
||||
for (unsigned plane = 0; plane < planes; ++plane)
|
||||
nonzero |= packet[planeOffset++];
|
||||
signs += networkPyroWaveBitCount(nonzero);
|
||||
}
|
||||
}
|
||||
|
||||
const size_t signBytes = (signs + 7U) / 8U;
|
||||
if (signBytes > packetSize - planeOffset)
|
||||
return false;
|
||||
|
||||
/* payload_words rounds the packet to a u32 boundary. No other trailing
|
||||
* payload is defined by version 1. */
|
||||
return packetSize - planeOffset - signBytes < sizeof(uint32_t);
|
||||
}
|
||||
|
||||
static uint32_t networkPyroWaveAlign(uint32_t value, uint32_t alignment)
|
||||
{
|
||||
return (value + alignment - 1U) & ~(alignment - 1U);
|
||||
}
|
||||
|
||||
static bool networkPyroWaveBlockCapacity(
|
||||
const LGNetVideoStreamConfig * config, uint32_t * capacity)
|
||||
{
|
||||
if (!config || !capacity)
|
||||
return false;
|
||||
|
||||
uint32_t width = networkPyroWaveAlign(
|
||||
config->width, NETWORK_PYROWAVE_ALIGNMENT);
|
||||
uint32_t height = networkPyroWaveAlign(
|
||||
config->height, NETWORK_PYROWAVE_ALIGNMENT);
|
||||
if (width < NETWORK_PYROWAVE_MINIMUM_SIZE)
|
||||
width = NETWORK_PYROWAVE_MINIMUM_SIZE;
|
||||
if (height < NETWORK_PYROWAVE_MINIMUM_SIZE)
|
||||
height = NETWORK_PYROWAVE_MINIMUM_SIZE;
|
||||
|
||||
uint64_t blocks = 0;
|
||||
for (int level = NETWORK_PYROWAVE_DECOMPOSITION_LEVELS - 1;
|
||||
level >= 0; --level)
|
||||
{
|
||||
const uint32_t levelWidth = width >> (level + 1);
|
||||
const uint32_t levelHeight = height >> (level + 1);
|
||||
const uint32_t blocksWide = (levelWidth + 31U) / 32U;
|
||||
const uint32_t blocksHigh = (levelHeight + 31U) / 32U;
|
||||
const uint32_t bands =
|
||||
level == NETWORK_PYROWAVE_DECOMPOSITION_LEVELS - 1 ? 4U : 3U;
|
||||
uint32_t components = 3U;
|
||||
if (!level && config->chromaSubsampling == LG_NET_VIDEO_CHROMA_420)
|
||||
components = 1U;
|
||||
|
||||
blocks += (uint64_t)blocksWide * blocksHigh * bands * components;
|
||||
}
|
||||
|
||||
if (!blocks || blocks > LG_NET_VIDEO_MAX_BLOCKS)
|
||||
return false;
|
||||
|
||||
*capacity = (uint32_t)blocks;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool networkPyroWaveProfileValid(const LGNetVideoStreamConfig * config)
|
||||
{
|
||||
if (!config || !lgNetVideoStreamConfigValid(config) ||
|
||||
config->codec != LG_NET_VIDEO_CODEC_PYROWAVE ||
|
||||
config->codecVersion < LG_NET_VIDEO_PYROWAVE_CODEC_VERSION_MIN ||
|
||||
config->codecVersion > LG_NET_VIDEO_PYROWAVE_CODEC_VERSION_MAX)
|
||||
return false;
|
||||
|
||||
if (config->flags & LG_NET_VIDEO_STREAM_RESERVED_ALPHA)
|
||||
return false;
|
||||
|
||||
const bool hdr =
|
||||
(config->flags & LG_NET_VIDEO_STREAM_HDR) != 0;
|
||||
if (hdr ?
|
||||
(config->colorPrimaries != LG_NET_COLOR_PRIMARIES_BT2020 ||
|
||||
config->colorTransfer != LG_NET_COLOR_TRANSFER_PQ ||
|
||||
config->colorMatrix != LG_NET_COLOR_MATRIX_BT2020) :
|
||||
(config->colorPrimaries != LG_NET_COLOR_PRIMARIES_BT709 ||
|
||||
config->colorTransfer != LG_NET_COLOR_TRANSFER_BT709 ||
|
||||
config->colorMatrix != LG_NET_COLOR_MATRIX_BT709))
|
||||
return false;
|
||||
|
||||
uint32_t capacity;
|
||||
return networkPyroWaveBlockCapacity(config, &capacity);
|
||||
}
|
||||
|
||||
static bool networkPyroWaveSequenceProfile(
|
||||
const LGNetVideoStreamConfig * config, uint32_t word)
|
||||
{
|
||||
const uint32_t chroma = (word >> 26) & 1U;
|
||||
const uint32_t primaries = (word >> 27) & 1U;
|
||||
const uint32_t transfer = (word >> 28) & 1U;
|
||||
const uint32_t transform = (word >> 29) & 1U;
|
||||
const uint32_t range = (word >> 30) & 1U;
|
||||
const uint32_t wireChroma =
|
||||
config->chromaSubsampling == LG_NET_VIDEO_CHROMA_444 ? 1U : 0U;
|
||||
const uint32_t wirePrimaries =
|
||||
config->colorPrimaries == LG_NET_COLOR_PRIMARIES_BT2020 ? 1U : 0U;
|
||||
const uint32_t wireTransfer =
|
||||
config->colorTransfer == LG_NET_COLOR_TRANSFER_PQ ? 1U : 0U;
|
||||
const uint32_t wireTransform =
|
||||
config->colorMatrix == LG_NET_COLOR_MATRIX_BT2020 ? 1U : 0U;
|
||||
const uint32_t wireRange =
|
||||
config->colorRange == LG_NET_COLOR_RANGE_LIMITED ? 1U : 0U;
|
||||
|
||||
return chroma == wireChroma &&
|
||||
primaries == wirePrimaries &&
|
||||
transfer == wireTransfer &&
|
||||
transform == wireTransform &&
|
||||
range == wireRange;
|
||||
}
|
||||
|
||||
NetworkPyroWaveResult networkPyroWaveValidate(
|
||||
const LGNetVideoStreamConfig * config, const void * data, size_t size,
|
||||
uint32_t expectedBlocks, uint8_t * sequence)
|
||||
{
|
||||
if (!networkPyroWaveProfileValid(config))
|
||||
return NETWORK_PYROWAVE_UNSUPPORTED;
|
||||
if (!data || size < NETWORK_PYROWAVE_HEADER_SIZE || !expectedBlocks)
|
||||
return NETWORK_PYROWAVE_INVALID;
|
||||
|
||||
uint32_t capacity;
|
||||
if (!networkPyroWaveBlockCapacity(config, &capacity) ||
|
||||
expectedBlocks > capacity)
|
||||
return NETWORK_PYROWAVE_INVALID;
|
||||
|
||||
const size_t seenSize = ((size_t)capacity + 7U) / 8U;
|
||||
uint8_t * seen = calloc(1, seenSize);
|
||||
if (!seen)
|
||||
return NETWORK_PYROWAVE_NO_MEMORY;
|
||||
|
||||
const uint8_t * bytes = data;
|
||||
size_t remaining = size;
|
||||
uint32_t uniqueBlocks = 0;
|
||||
uint32_t declaredBlocks = 0;
|
||||
uint8_t frameSequence = 0;
|
||||
bool haveSequence = false;
|
||||
bool haveHeader = false;
|
||||
bool valid = true;
|
||||
|
||||
while (valid && remaining >= NETWORK_PYROWAVE_HEADER_SIZE)
|
||||
{
|
||||
const uint32_t first = networkPyroWaveReadU32(bytes);
|
||||
const uint32_t second = networkPyroWaveReadU32(bytes + 4U);
|
||||
const uint8_t packetSequence =
|
||||
(uint8_t)((first >> 28) & NETWORK_PYROWAVE_SEQUENCE_MASK);
|
||||
const bool extended = (first >> 31) != 0;
|
||||
|
||||
if (haveSequence && packetSequence != frameSequence)
|
||||
{
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
haveSequence = true;
|
||||
frameSequence = packetSequence;
|
||||
|
||||
if (extended)
|
||||
{
|
||||
const uint32_t code = (second >> 24) & 3U;
|
||||
const uint32_t width = (first & UINT32_C(0x00003FFF)) + 1U;
|
||||
const uint32_t height = ((first >> 14) & UINT32_C(0x00003FFF)) + 1U;
|
||||
const uint32_t blocks = second & UINT32_C(0x00FFFFFF);
|
||||
if (code || width != config->width || height != config->height ||
|
||||
!blocks || blocks > capacity ||
|
||||
!networkPyroWaveSequenceProfile(config, second) ||
|
||||
(haveHeader && blocks != declaredBlocks))
|
||||
{
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
declaredBlocks = blocks;
|
||||
haveHeader = true;
|
||||
bytes += NETWORK_PYROWAVE_HEADER_SIZE;
|
||||
remaining -= NETWORK_PYROWAVE_HEADER_SIZE;
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint32_t ballot = first & UINT32_C(0x0000FFFF);
|
||||
const uint32_t payloadWords = (first >> 16) & UINT32_C(0x00000FFF);
|
||||
const uint32_t blockIndex = second >> 8;
|
||||
const size_t packetSize = (size_t)payloadWords * sizeof(uint32_t);
|
||||
if (!ballot || payloadWords < 2U || packetSize > remaining ||
|
||||
blockIndex >= capacity ||
|
||||
!networkPyroWavePacketValid(bytes, packetSize, (uint16_t)ballot))
|
||||
{
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
const uint8_t mask = (uint8_t)(1U << (blockIndex & 7U));
|
||||
if (!(seen[blockIndex >> 3] & mask))
|
||||
{
|
||||
seen[blockIndex >> 3] |= mask;
|
||||
++uniqueBlocks;
|
||||
}
|
||||
bytes += packetSize;
|
||||
remaining -= packetSize;
|
||||
}
|
||||
|
||||
valid = valid && !remaining && haveHeader && haveSequence &&
|
||||
declaredBlocks == uniqueBlocks && expectedBlocks == uniqueBlocks;
|
||||
free(seen);
|
||||
if (!valid)
|
||||
return NETWORK_PYROWAVE_INVALID;
|
||||
|
||||
if (sequence)
|
||||
*sequence = frameSequence;
|
||||
return NETWORK_PYROWAVE_OK;
|
||||
}
|
||||
42
client/transports/Network/video/pyrowave.h
Normal file
42
client/transports/Network/video/pyrowave.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* 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 2 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, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LGProtocol/NetworkServices.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef enum NetworkPyroWaveResult
|
||||
{
|
||||
NETWORK_PYROWAVE_OK,
|
||||
NETWORK_PYROWAVE_INVALID,
|
||||
NETWORK_PYROWAVE_UNSUPPORTED,
|
||||
NETWORK_PYROWAVE_NO_MEMORY,
|
||||
}
|
||||
NetworkPyroWaveResult;
|
||||
|
||||
bool networkPyroWaveProfileValid(const LGNetVideoStreamConfig * config);
|
||||
|
||||
NetworkPyroWaveResult networkPyroWaveValidate(
|
||||
const LGNetVideoStreamConfig * config, const void * data, size_t size,
|
||||
uint32_t expectedBlocks, uint8_t * sequence);
|
||||
332
client/transports/Network/video/shaders.h
Normal file
332
client/transports/Network/video/shaders.h
Normal file
@@ -0,0 +1,332 @@
|
||||
/**
|
||||
* Looking Glass
|
||||
* Copyright © 2017-2026 The Looking Glass Authors
|
||||
* https://looking-glass.io
|
||||
*
|
||||
* 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 2 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, write to the Free Software Foundation, Inc., 59
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* The coefficient and inverse-wavelet shaders are OpenGL ES adaptations of
|
||||
* PyroWave's Vulkan shaders at commit
|
||||
* 89f7e47d4abbf650c91fae766728af866c5e32a0.
|
||||
*
|
||||
* Copyright (c) 2025 Hans-Kristian Arntzen
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
static const char NETWORK_VIDEO_DEQUANT_SHADER_0[] =
|
||||
"#version 310 es\n"
|
||||
"precision highp float;\n"
|
||||
"precision highp int;\n"
|
||||
"layout(local_size_x=128) in;\n"
|
||||
"layout(r32f,binding=0) writeonly uniform highp image2DArray outputImage;\n"
|
||||
"layout(std430,binding=0) readonly buffer OffsetBuffer { uint offsets[]; };\n"
|
||||
"layout(std430,binding=1) readonly buffer PayloadBuffer { uint payload[]; };\n"
|
||||
"uniform ivec2 resolution;\n"
|
||||
"uniform int outputLayer;\n"
|
||||
"uniform int blockOffset;\n"
|
||||
"uniform int blockStride;\n"
|
||||
"shared uint signOffset;\n"
|
||||
"shared uint planeOffset[16];\n"
|
||||
"shared uint significantPrefix[128];\n"
|
||||
"uint readU8(uint byteOffset) {\n"
|
||||
" return (payload[byteOffset >> 2u] >> (8u * (byteOffset & 3u))) & 255u;\n"
|
||||
"}\n"
|
||||
"uint readU16(uint byteOffset) {\n"
|
||||
" return readU8(byteOffset) | (readU8(byteOffset + 1u) << 8u);\n"
|
||||
"}\n"
|
||||
"ivec2 unswizzle8x8(uint index) {\n"
|
||||
" uint y = bitfieldExtract(index, 0, 1);\n"
|
||||
" uint x = bitfieldExtract(index, 1, 2);\n"
|
||||
" y |= bitfieldExtract(index, 3, 2) << 1;\n"
|
||||
" x |= bitfieldExtract(index, 5, 1) << 2;\n"
|
||||
" return ivec2(x, y);\n"
|
||||
"}\n"
|
||||
"float quantScale(uint code) { return float(code) / 8.0 + 0.25; }\n"
|
||||
"float blockScale(uint code) {\n"
|
||||
" int exponent = 4 - int(code >> 3u);\n"
|
||||
" return (float(8u + (code & 7u)) / 8.0) * exp2(float(exponent));\n"
|
||||
"}\n"
|
||||
"void storeValues(ivec2 coord, mat2x4 values) {\n"
|
||||
" for (int y=0; y<2; ++y) for (int x=0; x<4; ++x) {\n"
|
||||
" ivec2 target = coord + ivec2(x,y);\n"
|
||||
" if (all(lessThan(target,resolution)))\n"
|
||||
" imageStore(outputImage,ivec3(target,outputLayer),vec4(values[y][x]));\n"
|
||||
" }\n"
|
||||
"}\n"
|
||||
"void main() {\n"
|
||||
" uint localIndex = gl_LocalInvocationIndex;\n"
|
||||
" int blockIndex = blockOffset + int(gl_WorkGroupID.y) * blockStride +\n"
|
||||
" int(gl_WorkGroupID.x);\n"
|
||||
" uint sourceWord = offsets[blockIndex];\n"
|
||||
" uint subblock = bitfieldExtract(localIndex,0,3);\n"
|
||||
" uint blockX = bitfieldExtract(localIndex,3,2);\n"
|
||||
" uint blockY = bitfieldExtract(localIndex,5,2);\n"
|
||||
" uint group = blockY * 4u + blockX;\n"
|
||||
" ivec2 coord = ivec2(gl_WorkGroupID.xy) * 32 +\n"
|
||||
" 8 * ivec2(blockX,blockY) + unswizzle8x8(subblock << 3u);\n"
|
||||
" if (sourceWord == 0xffffffffu) {\n"
|
||||
" storeValues(coord,mat2x4(vec4(0.0),vec4(0.0)));\n"
|
||||
" return;\n"
|
||||
" }\n"
|
||||
" uint sourceByte = sourceWord * 4u;\n"
|
||||
" uint ballot = payload[sourceWord] & 65535u;\n"
|
||||
" if (localIndex == 0u) {\n"
|
||||
" uint cursor = sourceByte + 8u + 3u * uint(bitCount(ballot));\n"
|
||||
" for (uint index=0u; index<16u; ++index) {\n"
|
||||
" planeOffset[index] = cursor;\n"
|
||||
" if (bitfieldExtract(ballot,int(index),1) != 0u) {\n"
|
||||
" uint compact = uint(bitCount(bitfieldExtract(ballot,0,int(index))));\n"
|
||||
" uint control = readU16(sourceByte + 8u + 2u * compact);\n"
|
||||
" uint qbits = readU8(sourceByte + 8u +\n"
|
||||
" 2u * uint(bitCount(ballot)) + compact) & 15u;\n"
|
||||
" uint low = control & 0x5555u;\n"
|
||||
" uint high = control & 0xaaaau;\n"
|
||||
" high |= high >> 1u;\n"
|
||||
" cursor += uint(bitCount(low) + bitCount(high)) + 8u * qbits;\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" signOffset = cursor * 8u;\n"
|
||||
" }\n"
|
||||
" barrier();\n";
|
||||
|
||||
static const char NETWORK_VIDEO_DEQUANT_SHADER_1[] =
|
||||
" mat2x4 values = mat2x4(vec4(0.0),vec4(0.0));\n"
|
||||
" uint significant = 0u;\n"
|
||||
" if (bitfieldExtract(ballot,int(group),1) != 0u) {\n"
|
||||
" uint compact = uint(bitCount(bitfieldExtract(ballot,0,int(group))));\n"
|
||||
" uint control = readU16(sourceByte + 8u + 2u * compact);\n"
|
||||
" uint scaleCode = readU8(sourceByte + 8u +\n"
|
||||
" 2u * uint(bitCount(ballot)) + compact);\n"
|
||||
" uint planes = (scaleCode & 15u) +\n"
|
||||
" bitfieldExtract(control,int(2u * subblock),2);\n"
|
||||
" int magnitude[8];\n"
|
||||
" for (int i=0; i<8; ++i) magnitude[i]=0;\n"
|
||||
" uint cursor = planeOffset[group];\n"
|
||||
" for (int plane=int(planes)-1; plane>=0; --plane) {\n"
|
||||
" uint bits = readU8(cursor++);\n"
|
||||
" for (int i=0; i<8; ++i)\n"
|
||||
" magnitude[i] = (magnitude[i] << 1) | int((bits >> uint(i)) & 1u);\n"
|
||||
" }\n"
|
||||
" float scale = blockScale(payload[sourceWord+1u] & 255u) *\n"
|
||||
" quantScale(scaleCode >> 4u);\n"
|
||||
" for (int x=0; x<4; ++x) for (int y=0; y<2; ++y) {\n"
|
||||
" int index = x * 2 + y;\n"
|
||||
" float value = float(magnitude[index]);\n"
|
||||
" if (value != 0.0) { value += 0.5; ++significant; }\n"
|
||||
" values[y][x] = value * scale;\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" significantPrefix[localIndex] = significant;\n"
|
||||
" barrier();\n"
|
||||
" if (localIndex == 0u) {\n"
|
||||
" uint prefix = 0u;\n"
|
||||
" for (uint index=0u; index<128u; ++index) {\n"
|
||||
" uint count = significantPrefix[index];\n"
|
||||
" significantPrefix[index] = prefix;\n"
|
||||
" prefix += count;\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" barrier();\n"
|
||||
" uint bitCursor = signOffset + significantPrefix[localIndex];\n"
|
||||
" uint word = payload[bitCursor >> 5u];\n"
|
||||
" uint upper = payload[(bitCursor >> 5u) + 1u];\n"
|
||||
" uint shift = bitCursor & 31u;\n"
|
||||
" if (shift != 0u) word = (word >> shift) | (upper << (32u-shift));\n"
|
||||
" uint signIndex = 0u;\n"
|
||||
" for (int x=0; x<4; ++x) for (int y=0; y<2; ++y) {\n"
|
||||
" if (values[y][x] != 0.0) {\n"
|
||||
" if (((word >> signIndex) & 1u) != 0u) values[y][x] = -values[y][x];\n"
|
||||
" ++signIndex;\n"
|
||||
" }\n"
|
||||
" }\n"
|
||||
" storeValues(coord,values);\n"
|
||||
"}\n";
|
||||
|
||||
static const char NETWORK_VIDEO_IDWT_SHADER_0[] =
|
||||
"#version 310 es\n"
|
||||
"precision highp float;\n"
|
||||
"precision highp int;\n"
|
||||
"layout(local_size_x=64) in;\n"
|
||||
"layout(binding=0) uniform highp sampler2DArray coefficients;\n"
|
||||
"layout(r32f,binding=1) writeonly uniform highp image2D outputImage;\n"
|
||||
"uniform ivec2 resolution;\n"
|
||||
"uniform vec2 invResolution;\n"
|
||||
"uniform int inputLevel;\n"
|
||||
"uniform int baseLayer;\n"
|
||||
"uniform bool dcShift;\n"
|
||||
"const int APRON=4, HALF_APRON=2, BLOCK=32, HALF_BLOCK=16;\n"
|
||||
"const float ALPHA=-1.586134342059924;\n"
|
||||
"const float BETA=-0.052980118572961;\n"
|
||||
"const float GAMMA=0.882911075530934;\n"
|
||||
"const float DELTA=0.443506852043971;\n"
|
||||
"const float K=1.230174104914001;\n"
|
||||
"const float INV_K=1.0/1.230174104914001;\n"
|
||||
"shared vec2 sharedBlock[20][41];\n"
|
||||
"ivec2 unswizzle8x8(uint index) {\n"
|
||||
" uint y=bitfieldExtract(index,0,1), x=bitfieldExtract(index,1,2);\n"
|
||||
" y|=bitfieldExtract(index,3,2)<<1; x|=bitfieldExtract(index,5,1)<<2;\n"
|
||||
" return ivec2(x,y);\n"
|
||||
"}\n"
|
||||
"bvec2 and2(bvec2 a,bvec2 b) { return bvec2(a.x&&b.x,a.y&&b.y); }\n"
|
||||
"vec2 mirrorUV(ivec2 coord,bool evenX,bool evenY) {\n"
|
||||
" coord-=ivec2(and2(bvec2(evenX,evenY),lessThan(coord,ivec2(0))));\n"
|
||||
" coord+=1;\n"
|
||||
" coord+=ivec2(and2(bvec2(!evenX,!evenY),greaterThanEqual(coord,resolution)));\n"
|
||||
" return (vec2(coord)*invResolution).yx;\n"
|
||||
"}\n"
|
||||
"vec4 gatherBand(ivec2 coord,bool evenX,bool evenY,int band) {\n"
|
||||
" return textureGather(coefficients,vec3(mirrorUV(coord,evenX,evenY),\n"
|
||||
" float(baseLayer+band)),0);\n"
|
||||
"}\n"
|
||||
"void store4(ivec2 coord,vec4 a,vec4 b,vec4 c,vec4 d) {\n"
|
||||
" sharedBlock[coord.y][2*coord.x+0]=vec2(a.x,c.x);\n"
|
||||
" sharedBlock[coord.y][2*coord.x+1]=vec2(b.x,d.x);\n"
|
||||
" sharedBlock[coord.y][2*coord.x+2]=vec2(a.y,c.y);\n"
|
||||
" sharedBlock[coord.y][2*coord.x+3]=vec2(b.y,d.y);\n"
|
||||
" sharedBlock[coord.y+1][2*coord.x+0]=vec2(a.z,c.z);\n"
|
||||
" sharedBlock[coord.y+1][2*coord.x+1]=vec2(b.z,d.z);\n"
|
||||
" sharedBlock[coord.y+1][2*coord.x+2]=vec2(a.w,c.w);\n"
|
||||
" sharedBlock[coord.y+1][2*coord.x+3]=vec2(b.w,d.w);\n"
|
||||
"}\n"
|
||||
"void loadApron(uint index) {\n"
|
||||
" ivec2 base=ivec2(gl_WorkGroupID.xy)*HALF_BLOCK-HALF_APRON;\n"
|
||||
" ivec2 local=2*unswizzle8x8(index); ivec2 coord=base+local;\n"
|
||||
" store4(local,gatherBand(coord,true,true,0).wxzy,\n"
|
||||
" gatherBand(coord,false,true,2).wxzy,\n"
|
||||
" gatherBand(coord,true,false,1).wxzy,\n"
|
||||
" gatherBand(coord,false,false,3).wxzy);\n"
|
||||
" ivec2 horizontal=ivec2(HALF_BLOCK+2*int(index%2u),2*int(index/2u));\n"
|
||||
" if (horizontal.y<HALF_BLOCK+2*HALF_APRON) { coord=base+horizontal;\n"
|
||||
" store4(horizontal,gatherBand(coord,true,true,0).wxzy,\n"
|
||||
" gatherBand(coord,false,true,2).wxzy,\n"
|
||||
" gatherBand(coord,true,false,1).wxzy,\n"
|
||||
" gatherBand(coord,false,false,3).wxzy); }\n"
|
||||
" ivec2 vertical=horizontal.yx;\n"
|
||||
" if (vertical.x<HALF_BLOCK) { coord=base+vertical;\n"
|
||||
" store4(vertical,gatherBand(coord,true,true,0).wxzy,\n"
|
||||
" gatherBand(coord,false,true,2).wxzy,\n"
|
||||
" gatherBand(coord,true,false,1).wxzy,\n"
|
||||
" gatherBand(coord,false,false,3).wxzy); }\n"
|
||||
" barrier();\n"
|
||||
"}\n";
|
||||
|
||||
static const char NETWORK_VIDEO_IDWT_SHADER_1[] =
|
||||
"void inverse8x2(uint index) {\n"
|
||||
" vec2 values[16]; ivec2 local=ivec2(8*int(index%4u),int(index/4u));\n"
|
||||
" for(int i=0;i<16;i+=2){values[i]=sharedBlock[local.y][local.x+i]*K;\n"
|
||||
" values[i+1]=sharedBlock[local.y][local.x+i+1]*INV_K;}\n"
|
||||
" for(int i=2;i<15;i+=2)values[i]-=DELTA*(values[i-1]+values[i+1]);\n"
|
||||
" for(int i=3;i<14;i+=2)values[i]-=GAMMA*(values[i-1]+values[i+1]);\n"
|
||||
" for(int i=4;i<13;i+=2)values[i]-=BETA*(values[i-1]+values[i+1]);\n"
|
||||
" for(int i=5;i<12;i+=2)values[i]-=ALPHA*(values[i-1]+values[i+1]);\n"
|
||||
" barrier();\n"
|
||||
" for(int i=2;i<6;++i){vec2 a=values[2*i],b=values[2*i+1];\n"
|
||||
" int y=(local.x>>1)+(i-2); sharedBlock[y][2*local.y]=vec2(a.x,b.x);\n"
|
||||
" sharedBlock[y][2*local.y+1]=vec2(a.y,b.y);}\n"
|
||||
"}\n"
|
||||
"void inverse4x2(uint index,bool activeLane,int yOffset) {\n"
|
||||
" vec2 values[12]; ivec2 local=ivec2(4*int(index%8u),int(index/8u)+yOffset);\n"
|
||||
" if(activeLane){for(int i=0;i<12;i+=2){values[i]=sharedBlock[local.y][local.x+i]*K;\n"
|
||||
" values[i+1]=sharedBlock[local.y][local.x+i+1]*INV_K;}\n"
|
||||
" for(int i=2;i<11;i+=2)values[i]-=DELTA*(values[i-1]+values[i+1]);\n"
|
||||
" for(int i=3;i<10;i+=2)values[i]-=GAMMA*(values[i-1]+values[i+1]);\n"
|
||||
" for(int i=4;i<9;i+=2)values[i]-=BETA*(values[i-1]+values[i+1]);\n"
|
||||
" for(int i=5;i<8;i+=2)values[i]-=ALPHA*(values[i-1]+values[i+1]);}\n"
|
||||
" barrier();\n"
|
||||
" if(activeLane)for(int i=2;i<4;++i){vec2 a=values[2*i],b=values[2*i+1];\n"
|
||||
" int y=(local.x>>1)+(i-2); sharedBlock[y][2*local.y]=vec2(a.x,b.x);\n"
|
||||
" sharedBlock[y][2*local.y+1]=vec2(a.y,b.y);}\n"
|
||||
"}\n"
|
||||
"void main(){uint index=gl_LocalInvocationIndex;loadApron(index);\n"
|
||||
" inverse8x2(index);inverse4x2(index,index<32u,HALF_BLOCK);barrier();\n"
|
||||
" inverse8x2(index);barrier();ivec2 local=unswizzle8x8(index);\n"
|
||||
" ivec2 outputSize=imageSize(outputImage);\n"
|
||||
" for(int y=local.y;y<HALF_BLOCK;y+=8)for(int x=local.x;x<BLOCK;x+=8){\n"
|
||||
" vec2 value=sharedBlock[y][x];if(dcShift)value+=vec2(0.5);\n"
|
||||
" ivec2 a=ivec2(2*y,x)+BLOCK*ivec2(gl_WorkGroupID.yx);\n"
|
||||
" ivec2 b=a+ivec2(1,0);if(all(lessThan(a,outputSize)))\n"
|
||||
" imageStore(outputImage,a,vec4(value.x));\n"
|
||||
" if(all(lessThan(b,outputSize)))imageStore(outputImage,b,vec4(value.y));}\n"
|
||||
"}\n";
|
||||
|
||||
static const char NETWORK_VIDEO_YUV_SHADER[] =
|
||||
"#version 310 es\n"
|
||||
"precision highp float;\n"
|
||||
"precision highp int;\n"
|
||||
"layout(local_size_x=8,local_size_y=8) in;\n"
|
||||
"layout(binding=0) uniform highp sampler2D lumaPlane;\n"
|
||||
"layout(binding=1) uniform highp sampler2D cbPlane;\n"
|
||||
"layout(binding=2) uniform highp sampler2D crPlane;\n"
|
||||
"layout(rgba16f,binding=0) writeonly uniform highp image2D outputImage;\n"
|
||||
"uniform ivec2 outputSize;\n"
|
||||
"uniform bool bt2020;\n"
|
||||
"uniform bool chromaLeft;\n"
|
||||
"uniform float lumaOffset;\n"
|
||||
"uniform float lumaScale;\n"
|
||||
"uniform float chromaMidpoint;\n"
|
||||
"uniform float chromaScale;\n"
|
||||
"void main(){ivec2 pixel=ivec2(gl_GlobalInvocationID.xy);\n"
|
||||
" if(any(greaterThanEqual(pixel,outputSize)))return;\n"
|
||||
" vec2 uv=(vec2(pixel)+0.5)/vec2(outputSize);\n"
|
||||
" vec2 offset=chromaLeft?vec2(0.5/float(outputSize.x),0.0):vec2(0.0);\n"
|
||||
" float y=clamp((texture(lumaPlane,uv).x-lumaOffset)*lumaScale,0.0,1.0);\n"
|
||||
" float cb=clamp((texture(cbPlane,uv+offset).x-chromaMidpoint)*\n"
|
||||
" chromaScale,-0.5,0.5);\n"
|
||||
" float cr=clamp((texture(crPlane,uv+offset).x-chromaMidpoint)*\n"
|
||||
" chromaScale,-0.5,0.5);\n"
|
||||
" mat3 m=bt2020?mat3(vec3(1.0),vec3(0.0,-0.16455313,1.8814),\n"
|
||||
" vec3(1.4746,-0.57135313,0.0)):\n"
|
||||
" mat3(vec3(1.0),vec3(0.0,-0.13397432/0.7152,1.8556),\n"
|
||||
" vec3(1.5748,-0.33480248/0.7152,0.0));\n"
|
||||
" vec3 rgb=clamp(m*vec3(y,cb,cr),vec3(0.0),vec3(1.0));\n"
|
||||
" imageStore(outputImage,pixel,vec4(rgb,1.0));}\n";
|
||||
|
||||
static const char * const NETWORK_VIDEO_DEQUANT_SOURCES[] =
|
||||
{
|
||||
NETWORK_VIDEO_DEQUANT_SHADER_0,
|
||||
NETWORK_VIDEO_DEQUANT_SHADER_1,
|
||||
};
|
||||
|
||||
static const char * const NETWORK_VIDEO_IDWT_SOURCES[] =
|
||||
{
|
||||
NETWORK_VIDEO_IDWT_SHADER_0,
|
||||
NETWORK_VIDEO_IDWT_SHADER_1,
|
||||
};
|
||||
|
||||
static const char * const NETWORK_VIDEO_YUV_SOURCES[] =
|
||||
{
|
||||
NETWORK_VIDEO_YUV_SHADER,
|
||||
};
|
||||
Reference in New Issue
Block a user