Skip to content

Commit

Permalink
#511 upload pixel data with memcpy then CopyResource
Browse files Browse the repository at this point in the history
I have tried hard to use the CPU buffer directly as it is,
but can't seem to be able to map it properly so that CopyResource will accept it
  • Loading branch information
totaam committed Jan 22, 2025
1 parent 1c0b581 commit f68eb7c
Show file tree
Hide file tree
Showing 5 changed files with 264 additions and 57 deletions.
25 changes: 25 additions & 0 deletions xpra/codecs/amf/amf.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,23 @@ cdef extern from "core/Result.h":
AMF_VULKAN_FAILED


cdef inline MEMORY_TYPE_STR(AMF_MEMORY_TYPE memory):
return {
AMF_MEMORY_UNKNOWN: "UNKNOWN",
AMF_MEMORY_HOST: "HOST",
AMF_MEMORY_DX9: "DX9",
AMF_MEMORY_DX11: "DX11",
AMF_MEMORY_OPENCL: "OPENCL",
AMF_MEMORY_OPENGL: "OPENGL",
AMF_MEMORY_XV: "XV",
AMF_MEMORY_GRALLOC: "GRALLOC",
AMF_MEMORY_COMPUTE_FOR_DX9: "COMPUTE_FOR_DX9",
AMF_MEMORY_COMPUTE_FOR_DX11: "COMPUTE_FOR_DX11",
AMF_MEMORY_VULKAN: "VULKAN",
AMF_MEMORY_DX12: "DX12",
}.get(memory, "unknown")


cdef inline RESULT_STR(AMF_RESULT res):
return {
AMF_OK: "OK",
Expand Down Expand Up @@ -170,6 +187,8 @@ cdef extern from "core/Data.h":
ctypedef amf_size (*DATA_GETPROPERTYCOUNT)(AMFData* pThis)
ctypedef AMF_MEMORY_TYPE (*DATA_GETMEMORYTYPE)(AMFData* pThis)
ctypedef AMF_DATA_TYPE (*DATA_GETDATATYPE)(AMFData* pThis)
ctypedef AMF_RESULT (*DATA_CONVERT)(AMFData* pThis, AMF_MEMORY_TYPE type)
ctypedef AMF_RESULT (*DATA_INTEROP)(AMFData* pThis, AMF_MEMORY_TYPE type)

ctypedef struct AMFDataVtbl:
DATA_ACQUIRE Acquire
Expand All @@ -178,6 +197,8 @@ cdef extern from "core/Data.h":
DATA_GETPROPERTYCOUNT GetPropertyCount
DATA_GETMEMORYTYPE GetMemoryType
DATA_GETDATATYPE GetDataType
DATA_CONVERT Convert
DATA_INTEROP Interop

ctypedef struct AMFData:
const AMFDataVtbl *pVtbl
Expand Down Expand Up @@ -433,6 +454,8 @@ cdef extern from "core/Surface.h":
ctypedef AMFPlane* (*SURFACE_GETPLANEAT)(AMFSurface* pThis, amf_size index)
ctypedef AMFPlane* (*SURFACE_GETPLANE)(AMFSurface* pThis, AMF_PLANE_TYPE type)
ctypedef AMF_FRAME_TYPE (*SURFACE_GETFRAMETYPE)(AMFSurface* pThis)
ctypedef AMF_RESULT (*SURFACE_CONVERT)(AMFSurface* pThis, AMF_MEMORY_TYPE type)
ctypedef AMF_RESULT (*SURFACE_INTEROP)(AMFSurface* pThis, AMF_MEMORY_TYPE type)

ctypedef struct AMFSurfaceVtbl:
SURFACE_SETPROPERTY SetProperty
Expand All @@ -443,6 +466,8 @@ cdef extern from "core/Surface.h":
SURFACE_GETPLANEAT GetPlaneAt
SURFACE_GETPLANE GetPlane
SURFACE_GETFRAMETYPE GetFrameType
SURFACE_CONVERT Convert
SURFACE_INTEROP Interop

ctypedef struct AMFSurface:
const AMFSurfaceVtbl *pVtbl
Expand Down
4 changes: 3 additions & 1 deletion xpra/codecs/amf/common.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ from libc.stddef cimport wchar_t
from libc.stdint cimport uint64_t, uintptr_t

from xpra.codecs.amf.amf cimport (
AMF_RESULT, AMFFactory, AMFGuid,
AMF_RESULT, AMFFactory, AMFGuid, AMFSurface,
amf_uint32, amf_uint16, amf_uint8,
)

Expand All @@ -25,3 +25,5 @@ cdef void set_guid(AMFGuid *guid,
amf_uint8 _data45, amf_uint8 _data46, amf_uint8 _data47, amf_uint8 _data48)

cdef uint64_t get_c_version()

cdef void fill_nv12_surface(AMFSurface *surface, amf_uint8 Y, amf_uint8 U, amf_uint8 V)
28 changes: 26 additions & 2 deletions xpra/codecs/amf/common.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ from ctypes import CDLL, c_uint64, c_int, c_void_p, byref, POINTER

from libc.stddef cimport wchar_t
from libc.stdint cimport uint64_t, uintptr_t
from libc.string cimport memset

from xpra.codecs.amf.amf cimport (
AMF_RESULT, AMF_OK,
AMFFactory, AMFGuid, AMFTrace,
amf_uint32, amf_uint16, amf_uint8,
AMFFactory, AMFGuid, AMFTrace, AMFSurface, AMFPlane,
amf_uint32, amf_uint16, amf_uint8, amf_int32,
RESULT_STR,
)

Expand Down Expand Up @@ -121,3 +122,26 @@ cdef uint64_t get_c_version():
return 0
return int(version.value)


cdef void fill_nv12_surface(AMFSurface *surface, amf_uint8 Y, amf_uint8 U, amf_uint8 V):
cdef AMFPlane *planeY = surface.pVtbl.GetPlaneAt(surface, 0)
cdef amf_int32 widthY = planeY.pVtbl.GetWidth(planeY)
cdef amf_int32 heightY = planeY.pVtbl.GetHeight(planeY)
cdef amf_int32 lineY = planeY.pVtbl.GetHPitch(planeY)
cdef amf_uint8 *Ydata = <amf_uint8 *> planeY.pVtbl.GetNative(planeY)
cdef amf_int32 y
cdef amf_uint8 *line
for y in range(heightY):
line = Ydata + y * lineY
memset(line, Y, widthY)
cdef AMFPlane *planeUV = surface.pVtbl.GetPlaneAt(surface, 1)
cdef amf_int32 widthUV = planeUV.pVtbl.GetWidth(planeUV)
cdef amf_int32 heightUV = planeUV.pVtbl.GetHeight(planeUV)
cdef amf_int32 lineUV = planeUV.pVtbl.GetHPitch(planeUV)
cdef amf_uint8 *UVdata = <amf_uint8 *> planeUV.pVtbl.GetNative(planeUV)
cdef amf_int32 x
for y in range(heightUV):
line = UVdata + y * lineUV
for x in range(widthUV):
line[x] = U
line[x+1] = V
94 changes: 58 additions & 36 deletions xpra/codecs/amf/encoder.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,14 @@ from xpra.log import Logger

from libc.stddef cimport wchar_t
from libc.stdint cimport uint8_t, int64_t, uintptr_t
from libc.string cimport memset
from libc.string cimport memset, memcpy

from xpra.codecs.amf.amf cimport (
AMF_PLANE_TYPE_STR, AMF_SURFACE_FORMAT_STR,
AMF_FRAME_TYPE, AMF_FRAME_TYPE_STR,
amf_uint8,
AMF_PLANE_TYPE_STR, AMF_SURFACE_FORMAT_STR, AMF_FRAME_TYPE, AMF_FRAME_TYPE_STR, MEMORY_TYPE_STR,
AMF_RESULT, AMF_EOF, AMF_REPEAT,
AMF_DX11_0,
AMF_MEMORY_TYPE,
AMF_MEMORY_DX11,
AMF_MEMORY_TYPE, AMF_MEMORY_DX11, AMF_MEMORY_HOST,
AMF_INPUT_FULL,
AMF_SURFACE_FORMAT, AMF_SURFACE_YUV420P, AMF_SURFACE_NV12, AMF_SURFACE_BGRA,
AMF_VARIANT_TYPE, AMF_VARIANT_INT64, AMF_VARIANT_SIZE, AMF_VARIANT_RATE,
Expand Down Expand Up @@ -230,11 +229,9 @@ cdef class Encoder:

self.context = NULL
self.encoder = NULL
self.surface = NULL
self.device = NULL

self.amf_context_init()
self.amf_surface_init()
self.amf_encoder_init(options)

self.generation = generation.increase()
Expand Down Expand Up @@ -273,13 +270,6 @@ cdef class Encoder:
self.device = <void *> self.context.pVtbl.GetOpenGLContext(self.context)
log(f"amf_context_init() device=%#x", <uintptr_t> self.device)

def amf_surface_init(self) -> None:
assert DX11
cdef AMF_MEMORY_TYPE memory = AMF_MEMORY_DX11
cdef AMF_RESULT res = self.context.pVtbl.AllocSurface(self.context, memory, self.surface_format, self.width, self.height, &self.surface)
self.check(res, "AMF surface initialization for {self.width}x{self.height} {self.src_format}")
log(f"amf_surface_init() surface=%s", self.get_surface_info(self.surface))

cdef void set_encoder_property(self, name: str, AMFVariantStruct var):
cdef wchar_t *prop = PyUnicode_AsWideCharString(name, NULL)
ret = self.encoder.pVtbl.SetProperty(self.encoder, prop, var)
Expand Down Expand Up @@ -403,11 +393,6 @@ cdef class Encoder:
self.clean()

def clean(self) -> None:
log("clean() surface=%s", <uintptr_t> self.surface)
cdef AMFSurface* surface = self.surface
if surface:
self.surface = NULL
surface.pVtbl.Release(surface)
log("clean() encoder=%s", <uintptr_t> self.encoder)
cdef AMFComponent* encoder = self.encoder
if encoder:
Expand Down Expand Up @@ -479,17 +464,42 @@ cdef class Encoder:
if py_buf[i].buf:
PyBuffer_Release(&py_buf[i])

cdef void set_surface_property(self, name: str, variant: AMF_VARIANT_TYPE, value: int64_t):
cdef void alloc_surface(self, AMFSurface **surface, AMF_MEMORY_TYPE memory=AMF_MEMORY_HOST):
assert self.context
memtype = MEMORY_TYPE_STR(memory)
cdef AMF_RESULT res = self.context.pVtbl.AllocSurface(self.context, memory, self.surface_format,
self.width, self.height, surface)
if res:
self.check(res, f"AMF {memtype} surface initialization for {self.width}x{self.height} {self.src_format}")
cdef AMFSurface *ptr = surface[0]
assert ptr != NULL
log(f"{memtype} surface: %s", self.get_surface_info(ptr))

cdef void set_surface_property(self, AMFSurface *surface, name: str, variant: AMF_VARIANT_TYPE, value: int64_t):
cdef AMFVariantStruct var
AMFVariantAssignInt64(&var, value)
cdef wchar_t *prop = PyUnicode_AsWideCharString(name, NULL)
self.surface.pVtbl.SetProperty(self.surface, prop, var)
surface.pVtbl.SetProperty(surface, prop, var)
PyMem_Free(prop)

cdef uintptr_t get_native_plane(self, AMFSurface *surface, unsigned int plane_index):
# get the D3D11 host destination surface pointer for this plane:
plane = surface.pVtbl.GetPlaneAt(surface, plane_index)
assert plane
log("plane=%s", self.get_plane_info(plane))
# texture is a `ID3D11Texture2D`:
cdef uintptr_t texture = <uintptr_t> plane.pVtbl.GetNative(plane)
assert texture
return texture

cdef bytes do_compress_image(self, uint8_t *pic_in[2], int strides[2]):
cdef unsigned long start_time = 0 # nanoseconds!
cdef AMFPlane *plane
cdef uintptr_t dst_texture
cdef uintptr_t host_texture
cdef uintptr_t gpu_texture
cdef AMFSurface *host_surface
cdef AMFSurface *gpu_surface
cdef AMF_RESULT res

if not WIN32:
raise ImportError("amf encoder needs porting to this platform")
Expand All @@ -500,24 +510,36 @@ cdef class Encoder:
with device.get_device_context() as dc:
log("device: %s", device.get_info())
log("device context: %s", dc.get_info())
log("surface: %s", self.get_surface_info(self.surface))

self.alloc_surface(&host_surface, AMF_MEMORY_HOST)

for plane_index in range(2):
log("plane %s src data=%#x", ["Y", "UV"][plane_index], <uintptr_t> pic_in[plane_index])
host_texture = self.get_native_plane(host_surface, plane_index)
memcpy(<void *> host_texture, <void *> pic_in[plane_index], strides[plane_index] * (self.height//2))

# make it accessible by the GPU:
res = host_surface.pVtbl.Convert(host_surface, AMF_MEMORY_DX11)
self.check(res, "AMF Convert to DX11 memory")

self.alloc_surface(&gpu_surface, AMF_MEMORY_DX11)

for plane_index in range(2):
# get the D3D11 destination surface pointer for this plane:
plane = self.surface.pVtbl.GetPlaneAt(self.surface, plane_index)
assert plane
log("plane %s=%s", ["Y", "UV"][plane_index], self.get_plane_info(plane))
# texture is a `ID3D11Texture2D`:
dst_texture = <uintptr_t> plane.pVtbl.GetNative(plane)
log("texture=%#x, source=%#x", dst_texture, <uintptr_t> pic_in[plane_index])
assert dst_texture
# dc.update_2dtexture(dst_texture, self.width, self.height,
# <uintptr_t> pic_in[plane_index], strides[plane_index])
log("plane %s", ["Y", "UV"][plane_index])
host_texture = self.get_native_plane(host_surface, plane_index)
gpu_texture = self.get_native_plane(gpu_surface, plane_index)
dc.copy_resource(gpu_texture, host_texture)
log("flush()")
dc.flush()
log("freeing host surface=%s", <uintptr_t> host_surface)
host_surface.pVtbl.Release(host_surface)

ns = round(1000 * 1000 * monotonic())
self.set_surface_property(START_TIME_PROPERTY, AMF_VARIANT_INT64, ns)
self.set_surface_property(gpu_surface, START_TIME_PROPERTY, AMF_VARIANT_INT64, ns)

cdef AMF_RESULT res = self.encoder.pVtbl.SubmitInput(self.encoder, <AMFData*> self.surface)
log("encoder.SubmitInput()")
res = self.encoder.pVtbl.SubmitInput(self.encoder, <AMFData*> gpu_surface)
gpu_surface.pVtbl.Release(gpu_surface)
if res == AMF_INPUT_FULL:
raise RuntimeError("AMF encoder input is full!")
self.check(res, "AMF submitting input to the encoder")
Expand All @@ -542,7 +564,7 @@ cdef class Encoder:
cdef size_t size = 0
try:
res = data.pVtbl.QueryInterface(data, &guid, <void**> &buffer)
log(f"QueryInterface()={res}")
log(f"QueryInterface()={res} AMFBuffer=%#x", <uintptr_t> buffer)
self.check(res, "AMF data query interface")
assert buffer != NULL
output = <uint8_t*> buffer.pVtbl.GetNative(buffer)
Expand Down
Loading

0 comments on commit f68eb7c

Please sign in to comment.