| /********************************************************** |
| * Copyright 2009-2015 VMware, Inc. All rights reserved. |
| * |
| * 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. |
| * |
| **********************************************************/ |
| |
| /** |
| * @file |
| * This file implements the SVGA interface into this winsys, defined |
| * in drivers/svga/svga_winsys.h. |
| * |
| * @author Keith Whitwell |
| * @author Jose Fonseca |
| */ |
| |
| #include <libsync.h> |
| #include <stdint.h> |
| #include <sys/ioctl.h> |
| #include <sys/mman.h> |
| |
| #include "svga_cmd.h" |
| #include "svga3d_caps.h" |
| |
| #include "c11/threads.h" |
| #include "util/os_file.h" |
| #include "util/u_inlines.h" |
| #include "util/u_math.h" |
| #include "util/u_memory.h" |
| #include "pipebuffer/pb_buffer.h" |
| #include "pipebuffer/pb_bufmgr.h" |
| #include "svga_winsys.h" |
| #include "vmw_context.h" |
| #include "vmw_screen.h" |
| #include "vmw_surface.h" |
| #include "vmw_buffer.h" |
| #include "vmw_fence.h" |
| #include "vmw_msg.h" |
| #include "vmw_shader.h" |
| #include "vmw_query.h" |
| #include "vmwgfx_drm.h" |
| #include "svga3d_surfacedefs.h" |
| #include "xf86drm.h" |
| |
| /** |
| * Try to get a surface backing buffer from the cache |
| * if it's this size or smaller. |
| */ |
| #define VMW_TRY_CACHED_SIZE (2*1024*1024) |
| |
| #ifdef VMX86_STATS |
| static const char* const vmw_svga_winsys_stats_count_names[] = { |
| SVGA_STATS_COUNT_NAMES |
| }; |
| |
| static const char* const vmw_svga_winsys_stats_time_names[] = { |
| SVGA_STATS_TIME_NAMES |
| }; |
| |
| /* |
| * It's imperative that the above two arrays are const, so that the next |
| * function can be optimized to a constant. |
| */ |
| static inline size_t |
| vmw_svga_winsys_stats_names_len(void) |
| { |
| size_t i, res = 0; |
| for (i = 0; i < ARRAY_SIZE(vmw_svga_winsys_stats_count_names); ++i) |
| res += strlen(vmw_svga_winsys_stats_count_names[i]) + 1; |
| for (i = 0; i < ARRAY_SIZE(vmw_svga_winsys_stats_time_names); ++i) |
| res += strlen(vmw_svga_winsys_stats_time_names[i]) + 1; |
| return res; |
| } |
| |
| typedef struct Atomic_uint64 { |
| uint64_t value; |
| } Atomic_uint64; |
| |
| typedef struct MKSGuestStatCounter { |
| Atomic_uint64 count; |
| } MKSGuestStatCounter; |
| |
| typedef struct MKSGuestStatCounterTime { |
| MKSGuestStatCounter counter; |
| Atomic_uint64 selfCycles; |
| Atomic_uint64 totalCycles; |
| } MKSGuestStatCounterTime; |
| |
| #define MKS_GUEST_STAT_FLAG_NONE 0 |
| #define MKS_GUEST_STAT_FLAG_TIME (1U << 0) |
| |
| typedef __attribute__((aligned(32))) struct MKSGuestStatInfoEntry { |
| union { |
| const char *s; |
| uint64_t u; |
| } name; |
| union { |
| const char *s; |
| uint64_t u; |
| } description; |
| uint64_t flags; |
| union { |
| MKSGuestStatCounter *counter; |
| MKSGuestStatCounterTime *counterTime; |
| uint64_t u; |
| } stat; |
| } MKSGuestStatInfoEntry; |
| |
| static thread_local struct svga_winsys_stats_timeframe *mksstat_tls_global = NULL; |
| |
| #define ALIGN(x, power_of_two) (((x) + (power_of_two) - 1) & ~((power_of_two) - 1)) |
| |
| static const size_t mksstat_area_size_info = sizeof(MKSGuestStatInfoEntry) * (SVGA_STATS_COUNT_MAX + SVGA_STATS_TIME_MAX); |
| static const size_t mksstat_area_size_stat = sizeof(MKSGuestStatCounter) * SVGA_STATS_COUNT_MAX + |
| sizeof(MKSGuestStatCounterTime) * SVGA_STATS_TIME_MAX; |
| |
| size_t |
| vmw_svga_winsys_stats_len(void) |
| { |
| const size_t pg_size = getpagesize(); |
| const size_t area_size_stat_pg = ALIGN(mksstat_area_size_stat, pg_size); |
| const size_t area_size_info_pg = ALIGN(mksstat_area_size_info, pg_size); |
| const size_t area_size_strs = vmw_svga_winsys_stats_names_len(); |
| const size_t area_size = area_size_stat_pg + area_size_info_pg + area_size_strs; |
| |
| return area_size; |
| } |
| |
| /** |
| * vmw_mksstat_get_pstat: Computes the address of the MKSGuestStatCounter |
| * array from the address of the base page. |
| * |
| * @page_addr: Pointer to the base page. |
| * @page_size: Size of page. |
| * Return: Pointer to the MKSGuestStatCounter array. |
| */ |
| |
| static inline MKSGuestStatCounter * |
| vmw_mksstat_get_pstat(uint8_t *page_addr, size_t page_size) |
| { |
| return (MKSGuestStatCounter *)page_addr; |
| } |
| |
| /** |
| * vmw_mksstat_get_pstat_time: Computes the address of the MKSGuestStatCounterTime |
| * array from the address of the base page. |
| * |
| * @page_addr: Pointer to the base page. |
| * @page_size: Size of page. |
| * Return: Pointer to the MKSGuestStatCounterTime array. |
| */ |
| |
| static inline MKSGuestStatCounterTime * |
| vmw_mksstat_get_pstat_time(uint8_t *page_addr, size_t page_size) |
| { |
| return (MKSGuestStatCounterTime *)(page_addr + sizeof(MKSGuestStatCounter) * SVGA_STATS_COUNT_MAX); |
| } |
| |
| /** |
| * vmw_mksstat_get_pinfo: Computes the address of the MKSGuestStatInfoEntry |
| * array from the address of the base page. |
| * |
| * @page_addr: Pointer to the base page. |
| * @page_size: Size of page. |
| * Return: Pointer to the MKSGuestStatInfoEntry array. |
| */ |
| |
| static inline MKSGuestStatInfoEntry * |
| vmw_mksstat_get_pinfo(uint8_t *page_addr, size_t page_size) |
| { |
| const size_t area_size_stat_pg = ALIGN(mksstat_area_size_stat, page_size); |
| return (MKSGuestStatInfoEntry *)(page_addr + area_size_stat_pg); |
| } |
| |
| /** |
| * vmw_mksstat_get_pstrs: Computes the address of the mksGuestStat strings |
| * sequence from the address of the base page. |
| * |
| * @page_addr: Pointer to the base page. |
| * @page_size: Size of page. |
| * Return: Pointer to the mksGuestStat strings sequence. |
| */ |
| |
| static inline char * |
| vmw_mksstat_get_pstrs(uint8_t *page_addr, const size_t page_size) |
| { |
| const size_t area_size_info_pg = ALIGN(mksstat_area_size_info, page_size); |
| const size_t area_size_stat_pg = ALIGN(mksstat_area_size_stat, page_size); |
| return (char *)(page_addr + area_size_info_pg + area_size_stat_pg); |
| } |
| |
| /** |
| * Add all known mksGuestStats counters for tracking by the host. |
| */ |
| static int |
| vmw_svga_winsys_add_stats(struct vmw_winsys_screen *vws, int slot) |
| { |
| const size_t pg_size = getpagesize(); |
| const size_t area_size = vmw_svga_winsys_stats_len(); |
| |
| MKSGuestStatInfoEntry *pinfo; |
| MKSGuestStatCounter *pstat; |
| MKSGuestStatCounterTime *pstatTime; |
| char *pstrs; |
| uint64_t id; |
| size_t i; |
| |
| /* Allocate a contiguous area of pages for all info entries, counters and strings. */ |
| void *area = mmap(NULL, area_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED | MAP_NORESERVE, -1, 0); |
| |
| if (area == MAP_FAILED) { |
| fprintf(stderr, "%s could not mmap memory: %s\n", __FUNCTION__, strerror(errno)); |
| return -1; |
| } |
| |
| pinfo = vmw_mksstat_get_pinfo(area, pg_size); |
| pstat = vmw_mksstat_get_pstat(area, pg_size); |
| pstrs = vmw_mksstat_get_pstrs(area, pg_size); |
| pstatTime = vmw_mksstat_get_pstat_time(area, pg_size); |
| |
| if (mlock(area, area_size)) { |
| fprintf(stderr, "%s could not mlock memory: %s\n", __FUNCTION__, strerror(errno)); |
| goto error; |
| } |
| |
| /* Suppress pages copy-on-write; for MAP_SHARED this should not really matter; it would if we go MAP_PRIVATE */ |
| if (madvise(area, area_size, MADV_DONTFORK)) { |
| fprintf(stderr, "%s could not madvise memory: %s\n", __FUNCTION__, strerror(errno)); |
| goto error; |
| } |
| |
| /* Set up regular counters first */ |
| for (i = 0; i < SVGA_STATS_COUNT_MAX; ++i) { |
| pinfo->name.s = pstrs; |
| pinfo->description.s = pstrs; |
| pinfo->flags = MKS_GUEST_STAT_FLAG_NONE; |
| pinfo->stat.counter = pstat + i; |
| pinfo++; |
| |
| memcpy(pstrs, vmw_svga_winsys_stats_count_names[i], strlen(vmw_svga_winsys_stats_count_names[i])); |
| pstrs += strlen(vmw_svga_winsys_stats_count_names[i]) + 1; |
| } |
| |
| /* Set up time counters second */ |
| for (i = 0; i < SVGA_STATS_TIME_MAX; ++i) { |
| pinfo->name.s = pstrs; |
| pinfo->description.s = pstrs; |
| pinfo->flags = MKS_GUEST_STAT_FLAG_TIME; |
| pinfo->stat.counterTime = pstatTime + i; |
| pinfo++; |
| |
| memcpy(pstrs, vmw_svga_winsys_stats_time_names[i], strlen(vmw_svga_winsys_stats_time_names[i])); |
| pstrs += strlen(vmw_svga_winsys_stats_time_names[i]) + 1; |
| } |
| |
| { /* ioctl(DRM_VMW_MKSSTAT_ADD) */ |
| char desc[64]; |
| snprintf(desc, sizeof(desc) - 1, "vmw_winsys_screen=%p pid=%d", vws, gettid()); |
| |
| struct drm_vmw_mksstat_add_arg arg = { |
| .stat = (uintptr_t)pstat, |
| .info = (uintptr_t)vmw_mksstat_get_pinfo(area, pg_size), |
| .strs = (uintptr_t)vmw_mksstat_get_pstrs(area, pg_size), |
| .stat_len = mksstat_area_size_stat, |
| .info_len = mksstat_area_size_info, |
| .strs_len = vmw_svga_winsys_stats_names_len(), |
| .description = (uintptr_t)desc, |
| .id = -1U |
| }; |
| if (drmCommandWriteRead(vws->ioctl.drm_fd, DRM_VMW_MKSSTAT_ADD, &arg, sizeof(arg))) { |
| fprintf(stderr, "%s could not ioctl: %s\n", __FUNCTION__, strerror(errno)); |
| goto error; |
| } |
| id = arg.id; |
| } |
| |
| vws->mksstat_tls[slot].stat_pages = area; |
| vws->mksstat_tls[slot].stat_id = id; |
| /* Don't update vws->mksstat_tls[].pid as it's reserved. */ |
| return 0; |
| |
| error: |
| munmap(area, area_size); |
| return -1; |
| } |
| |
| /** |
| * Acquire a mksstat TLS slot making it immutable by other parties. |
| */ |
| static inline int |
| vmw_winsys_screen_mksstat_acq_slot(struct vmw_winsys_screen *vws) |
| { |
| const pid_t pid = gettid(); |
| const size_t base = (size_t)pid % ARRAY_SIZE(vws->mksstat_tls); |
| size_t i; |
| |
| if (mksstat_tls_global && vmw_winsys_screen(mksstat_tls_global->sws) == vws) { |
| const size_t slot = mksstat_tls_global->slot; |
| uint32_t expecpid = pid; |
| if (__atomic_compare_exchange_n(&vws->mksstat_tls[slot].pid, &expecpid, -1U, false, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) |
| return (int)slot; |
| } |
| |
| for (i = 0; i < ARRAY_SIZE(vws->mksstat_tls); ++i) { |
| const size_t slot = (i + base) % ARRAY_SIZE(vws->mksstat_tls); |
| uint32_t expecpid = pid; |
| uint32_t expected = 0; |
| |
| /* Check if pid is already present */ |
| if (__atomic_compare_exchange_n(&vws->mksstat_tls[slot].pid, &expecpid, -1U, false, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) |
| return (int)slot; |
| |
| /* Try to set up a new mksstat for this pid */ |
| if (__atomic_compare_exchange_n(&vws->mksstat_tls[slot].pid, &expected, -1U, false, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) { |
| const int ret = vmw_svga_winsys_add_stats(vws, slot); |
| |
| if (!ret) |
| return (int)slot; |
| |
| __atomic_store_n(&vws->mksstat_tls[slot].pid, 0, __ATOMIC_RELEASE); |
| return ret; |
| } |
| } |
| |
| return -1; |
| } |
| |
| /** |
| * Release a mksstat TLS slot -- caller still owns the slot but now it is erasable by other parties. |
| */ |
| static inline void |
| vmw_winsys_screen_mksstat_rel_slot(struct vmw_winsys_screen *vws, int slot) |
| { |
| assert(slot < ARRAY_SIZE(vws->mksstat_tls)); |
| |
| __atomic_store_n(&vws->mksstat_tls[slot].pid, gettid(), __ATOMIC_RELEASE); |
| } |
| |
| static inline uint64_t |
| rdtsc(void) |
| { |
| uint32_t hi, lo; |
| __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); |
| return (uint64_t)lo | ((uint64_t)hi << 32); |
| } |
| |
| #endif /* VMX86_STATS */ |
| |
| static struct svga_winsys_buffer * |
| vmw_svga_winsys_buffer_create(struct svga_winsys_screen *sws, |
| unsigned alignment, |
| unsigned usage, |
| unsigned size) |
| { |
| struct vmw_winsys_screen *vws = vmw_winsys_screen(sws); |
| struct vmw_buffer_desc desc; |
| struct pb_manager *provider; |
| struct pb_buffer *buffer; |
| |
| memset(&desc, 0, sizeof desc); |
| desc.pb_desc.alignment = alignment; |
| desc.pb_desc.usage = usage; |
| |
| if (usage == SVGA_BUFFER_USAGE_PINNED) { |
| if (vws->pools.query_fenced == NULL && !vmw_query_pools_init(vws)) |
| return NULL; |
| provider = vws->pools.query_fenced; |
| } else if (usage == SVGA_BUFFER_USAGE_SHADER) { |
| provider = vws->pools.mob_shader_slab_fenced; |
| } else { |
| if (size > VMW_GMR_POOL_SIZE) |
| return NULL; |
| provider = vws->pools.gmr_fenced; |
| } |
| |
| assert(provider); |
| buffer = provider->create_buffer(provider, size, &desc.pb_desc); |
| |
| if(!buffer && provider == vws->pools.gmr_fenced) { |
| |
| assert(provider); |
| provider = vws->pools.gmr_slab_fenced; |
| buffer = provider->create_buffer(provider, size, &desc.pb_desc); |
| } |
| |
| if (!buffer) |
| return NULL; |
| |
| return vmw_svga_winsys_buffer_wrap(buffer); |
| } |
| |
| |
| static void |
| vmw_svga_winsys_fence_reference(struct svga_winsys_screen *sws, |
| struct pipe_fence_handle **pdst, |
| struct pipe_fence_handle *src) |
| { |
| struct vmw_winsys_screen *vws = vmw_winsys_screen(sws); |
| |
| vmw_fence_reference(vws, pdst, src); |
| } |
| |
| |
| static int |
| vmw_svga_winsys_fence_signalled(struct svga_winsys_screen *sws, |
| struct pipe_fence_handle *fence, |
| unsigned flag) |
| { |
| struct vmw_winsys_screen *vws = vmw_winsys_screen(sws); |
| |
| return vmw_fence_signalled(vws, fence, flag); |
| } |
| |
| |
| static int |
| vmw_svga_winsys_fence_finish(struct svga_winsys_screen *sws, |
| struct pipe_fence_handle *fence, |
| uint64_t timeout, |
| unsigned flag) |
| { |
| struct vmw_winsys_screen *vws = vmw_winsys_screen(sws); |
| |
| return vmw_fence_finish(vws, fence, timeout, flag); |
| } |
| |
| |
| static int |
| vmw_svga_winsys_fence_get_fd(struct svga_winsys_screen *sws, |
| struct pipe_fence_handle *fence, |
| boolean duplicate) |
| { |
| if (duplicate) |
| return os_dupfd_cloexec(vmw_fence_get_fd(fence)); |
| else |
| return vmw_fence_get_fd(fence); |
| } |
| |
| |
| static void |
| vmw_svga_winsys_fence_create_fd(struct svga_winsys_screen *sws, |
| struct pipe_fence_handle **fence, |
| int32_t fd) |
| { |
| *fence = vmw_fence_create(NULL, 0, 0, 0, os_dupfd_cloexec(fd)); |
| } |
| |
| static int |
| vmw_svga_winsys_fence_server_sync(struct svga_winsys_screen *sws, |
| int32_t *context_fd, |
| struct pipe_fence_handle *fence) |
| { |
| int32_t fd = sws->fence_get_fd(sws, fence, FALSE); |
| |
| /* If we don't have fd, we don't need to merge fd into the context's fd. */ |
| if (fd == -1) |
| return 0; |
| |
| return sync_accumulate("vmwgfx", context_fd, fd); |
| } |
| |
| |
| static struct svga_winsys_surface * |
| vmw_svga_winsys_surface_create(struct svga_winsys_screen *sws, |
| SVGA3dSurfaceAllFlags flags, |
| SVGA3dSurfaceFormat format, |
| unsigned usage, |
| SVGA3dSize size, |
| uint32 numLayers, |
| uint32 numMipLevels, |
| unsigned sampleCount) |
| { |
| struct vmw_winsys_screen *vws = vmw_winsys_screen(sws); |
| struct vmw_svga_winsys_surface *surface; |
| struct vmw_buffer_desc desc; |
| struct pb_manager *provider; |
| uint32_t buffer_size; |
| uint32_t num_samples = 1; |
| SVGA3dMSPattern multisample_pattern = SVGA3D_MS_PATTERN_NONE; |
| SVGA3dMSQualityLevel quality_level = SVGA3D_MS_QUALITY_NONE; |
| |
| memset(&desc, 0, sizeof(desc)); |
| surface = CALLOC_STRUCT(vmw_svga_winsys_surface); |
| if(!surface) |
| goto no_surface; |
| |
| pipe_reference_init(&surface->refcnt, 1); |
| p_atomic_set(&surface->validated, 0); |
| surface->screen = vws; |
| (void) mtx_init(&surface->mutex, mtx_plain); |
| surface->shared = !!(usage & SVGA_SURFACE_USAGE_SHARED); |
| provider = (surface->shared) ? vws->pools.gmr : vws->pools.mob_fenced; |
| |
| /* |
| * When multisampling is not supported sample count received is 0, |
| * otherwise should have a valid sample count. |
| */ |
| if ((flags & SVGA3D_SURFACE_MULTISAMPLE) != 0) { |
| if (sampleCount == 0) |
| goto no_sid; |
| num_samples = sampleCount; |
| multisample_pattern = SVGA3D_MS_PATTERN_STANDARD; |
| quality_level = SVGA3D_MS_QUALITY_FULL; |
| } |
| |
| /* |
| * Used for the backing buffer GB surfaces, and to approximate |
| * when to flush on non-GB hosts. |
| */ |
| buffer_size = svga3dsurface_get_serialized_size_extended(format, size, |
| numMipLevels, |
| numLayers, |
| num_samples); |
| if (flags & SVGA3D_SURFACE_BIND_STREAM_OUTPUT) |
| buffer_size += sizeof(SVGA3dDXSOState); |
| |
| if (buffer_size > vws->ioctl.max_texture_size) { |
| goto no_sid; |
| } |
| |
| if (sws->have_gb_objects) { |
| SVGAGuestPtr ptr = {0,0}; |
| |
| /* |
| * If the backing buffer size is small enough, try to allocate a |
| * buffer out of the buffer cache. Otherwise, let the kernel allocate |
| * a suitable buffer for us. |
| */ |
| if (buffer_size < VMW_TRY_CACHED_SIZE && !surface->shared) { |
| struct pb_buffer *pb_buf; |
| |
| surface->size = buffer_size; |
| desc.pb_desc.alignment = 4096; |
| desc.pb_desc.usage = 0; |
| pb_buf = provider->create_buffer(provider, buffer_size, &desc.pb_desc); |
| surface->buf = vmw_svga_winsys_buffer_wrap(pb_buf); |
| if (surface->buf && !vmw_gmr_bufmgr_region_ptr(pb_buf, &ptr)) |
| assert(0); |
| } |
| |
| surface->sid = vmw_ioctl_gb_surface_create(vws, flags, format, usage, |
| size, numLayers, |
| numMipLevels, sampleCount, |
| ptr.gmrId, |
| multisample_pattern, |
| quality_level, |
| surface->buf ? NULL : |
| &desc.region); |
| |
| if (surface->sid == SVGA3D_INVALID_ID) { |
| if (surface->buf == NULL) { |
| goto no_sid; |
| } else { |
| /* |
| * Kernel refused to allocate a surface for us. |
| * Perhaps something was wrong with our buffer? |
| * This is really a guard against future new size requirements |
| * on the backing buffers. |
| */ |
| vmw_svga_winsys_buffer_destroy(sws, surface->buf); |
| surface->buf = NULL; |
| surface->sid = vmw_ioctl_gb_surface_create(vws, flags, format, usage, |
| size, numLayers, |
| numMipLevels, sampleCount, |
| 0, multisample_pattern, |
| quality_level, |
| &desc.region); |
| if (surface->sid == SVGA3D_INVALID_ID) |
| goto no_sid; |
| } |
| } |
| |
| /* |
| * If the kernel created the buffer for us, wrap it into a |
| * vmw_svga_winsys_buffer. |
| */ |
| if (surface->buf == NULL) { |
| struct pb_buffer *pb_buf; |
| |
| surface->size = vmw_region_size(desc.region); |
| desc.pb_desc.alignment = 4096; |
| desc.pb_desc.usage = VMW_BUFFER_USAGE_SHARED; |
| pb_buf = provider->create_buffer(provider, surface->size, |
| &desc.pb_desc); |
| surface->buf = vmw_svga_winsys_buffer_wrap(pb_buf); |
| if (surface->buf == NULL) { |
| vmw_ioctl_region_destroy(desc.region); |
| vmw_ioctl_surface_destroy(vws, surface->sid); |
| goto no_sid; |
| } |
| } |
| } else { |
| /* Legacy surface only support 32-bit svga3d flags */ |
| surface->sid = vmw_ioctl_surface_create(vws, (SVGA3dSurface1Flags)flags, |
| format, usage, size, numLayers, |
| numMipLevels, sampleCount); |
| if(surface->sid == SVGA3D_INVALID_ID) |
| goto no_sid; |
| |
| /* Best estimate for surface size, used for early flushing. */ |
| surface->size = buffer_size; |
| surface->buf = NULL; |
| } |
| |
| return svga_winsys_surface(surface); |
| |
| no_sid: |
| if (surface->buf) |
| vmw_svga_winsys_buffer_destroy(sws, surface->buf); |
| |
| FREE(surface); |
| no_surface: |
| return NULL; |
| } |
| |
| static boolean |
| vmw_svga_winsys_surface_can_create(struct svga_winsys_screen *sws, |
| SVGA3dSurfaceFormat format, |
| SVGA3dSize size, |
| uint32 numLayers, |
| uint32 numMipLevels, |
| uint32 numSamples) |
| { |
| struct vmw_winsys_screen *vws = vmw_winsys_screen(sws); |
| uint32_t buffer_size; |
| |
| buffer_size = svga3dsurface_get_serialized_size(format, size, |
| numMipLevels, |
| numLayers); |
| if (numSamples > 1) |
| buffer_size *= numSamples; |
| |
| if (buffer_size > vws->ioctl.max_texture_size) { |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| |
| static boolean |
| vmw_svga_winsys_surface_is_flushed(struct svga_winsys_screen *sws, |
| struct svga_winsys_surface *surface) |
| { |
| struct vmw_svga_winsys_surface *vsurf = vmw_svga_winsys_surface(surface); |
| return (p_atomic_read(&vsurf->validated) == 0); |
| } |
| |
| |
| static void |
| vmw_svga_winsys_surface_ref(struct svga_winsys_screen *sws, |
| struct svga_winsys_surface **pDst, |
| struct svga_winsys_surface *src) |
| { |
| struct vmw_svga_winsys_surface *d_vsurf = vmw_svga_winsys_surface(*pDst); |
| struct vmw_svga_winsys_surface *s_vsurf = vmw_svga_winsys_surface(src); |
| |
| vmw_svga_winsys_surface_reference(&d_vsurf, s_vsurf); |
| *pDst = svga_winsys_surface(d_vsurf); |
| } |
| |
| |
| static void |
| vmw_svga_winsys_destroy(struct svga_winsys_screen *sws) |
| { |
| struct vmw_winsys_screen *vws = vmw_winsys_screen(sws); |
| |
| vmw_winsys_destroy(vws); |
| } |
| |
| |
| static SVGA3dHardwareVersion |
| vmw_svga_winsys_get_hw_version(struct svga_winsys_screen *sws) |
| { |
| struct vmw_winsys_screen *vws = vmw_winsys_screen(sws); |
| |
| if (sws->have_gb_objects) |
| return SVGA3D_HWVERSION_WS8_B1; |
| |
| return (SVGA3dHardwareVersion) vws->ioctl.hwversion; |
| } |
| |
| |
| static boolean |
| vmw_svga_winsys_get_cap(struct svga_winsys_screen *sws, |
| SVGA3dDevCapIndex index, |
| SVGA3dDevCapResult *result) |
| { |
| struct vmw_winsys_screen *vws = vmw_winsys_screen(sws); |
| |
| if (index > vws->ioctl.num_cap_3d || |
| index >= SVGA3D_DEVCAP_MAX || |
| !vws->ioctl.cap_3d[index].has_cap) |
| return FALSE; |
| |
| *result = vws->ioctl.cap_3d[index].result; |
| return TRUE; |
| } |
| |
| struct svga_winsys_gb_shader * |
| vmw_svga_winsys_shader_create(struct svga_winsys_screen *sws, |
| SVGA3dShaderType type, |
| const uint32 *bytecode, |
| uint32 bytecodeLen) |
| { |
| struct vmw_winsys_screen *vws = vmw_winsys_screen(sws); |
| struct vmw_svga_winsys_shader *shader; |
| void *code; |
| |
| shader = CALLOC_STRUCT(vmw_svga_winsys_shader); |
| if(!shader) |
| goto out_no_shader; |
| |
| pipe_reference_init(&shader->refcnt, 1); |
| p_atomic_set(&shader->validated, 0); |
| shader->screen = vws; |
| shader->buf = vmw_svga_winsys_buffer_create(sws, 64, |
| SVGA_BUFFER_USAGE_SHADER, |
| bytecodeLen); |
| if (!shader->buf) |
| goto out_no_buf; |
| |
| code = vmw_svga_winsys_buffer_map(sws, shader->buf, PIPE_MAP_WRITE); |
| if (!code) |
| goto out_no_buf; |
| |
| memcpy(code, bytecode, bytecodeLen); |
| vmw_svga_winsys_buffer_unmap(sws, shader->buf); |
| |
| if (!sws->have_vgpu10) { |
| shader->shid = vmw_ioctl_shader_create(vws, type, bytecodeLen); |
| if (shader->shid == SVGA3D_INVALID_ID) |
| goto out_no_shid; |
| } |
| |
| return svga_winsys_shader(shader); |
| |
| out_no_shid: |
| vmw_svga_winsys_buffer_destroy(sws, shader->buf); |
| out_no_buf: |
| FREE(shader); |
| out_no_shader: |
| return NULL; |
| } |
| |
| void |
| vmw_svga_winsys_shader_destroy(struct svga_winsys_screen *sws, |
| struct svga_winsys_gb_shader *shader) |
| { |
| struct vmw_svga_winsys_shader *d_shader = |
| vmw_svga_winsys_shader(shader); |
| |
| vmw_svga_winsys_shader_reference(&d_shader, NULL); |
| } |
| |
| #ifdef VMX86_STATS |
| static void |
| vmw_svga_winsys_stats_inc(struct svga_winsys_screen *sws, |
| enum svga_stats_count index) |
| { |
| struct vmw_winsys_screen *const vws = vmw_winsys_screen(sws); |
| const int slot = vmw_winsys_screen_mksstat_acq_slot(vws); |
| assert(index < SVGA_STATS_COUNT_MAX); |
| |
| if (slot >= 0) { |
| MKSGuestStatCounter *pstat; |
| assert(vws->mksstat_tls[slot].stat_pages); |
| assert(vws->mksstat_tls[slot].stat_id != -1UL); |
| |
| pstat = vmw_mksstat_get_pstat(vws->mksstat_tls[slot].stat_pages, getpagesize()); |
| |
| __atomic_fetch_add(&pstat[index].count.value, 1, __ATOMIC_ACQ_REL); |
| |
| vmw_winsys_screen_mksstat_rel_slot(vws, slot); |
| } |
| } |
| |
| static void |
| vmw_svga_winsys_stats_time_push(struct svga_winsys_screen *sws, |
| enum svga_stats_time index, |
| struct svga_winsys_stats_timeframe *tf) |
| { |
| struct vmw_winsys_screen *const vws = vmw_winsys_screen(sws); |
| const int slot = vmw_winsys_screen_mksstat_acq_slot(vws); |
| |
| if (slot < 0) |
| return; |
| |
| assert(vws->mksstat_tls[slot].stat_pages); |
| assert(vws->mksstat_tls[slot].stat_id != -1UL); |
| |
| tf->counterTime = vmw_mksstat_get_pstat_time(vws->mksstat_tls[slot].stat_pages, getpagesize()) + index; |
| |
| vmw_winsys_screen_mksstat_rel_slot(vws, slot); |
| |
| tf->startTime = rdtsc(); |
| tf->enclosing = mksstat_tls_global; |
| tf->sws = sws; |
| tf->slot = slot; |
| |
| mksstat_tls_global = tf; |
| } |
| |
| static void |
| vmw_svga_winsys_stats_time_pop(struct svga_winsys_screen *sws) |
| { |
| struct svga_winsys_stats_timeframe *const tf = mksstat_tls_global; |
| struct vmw_winsys_screen *const vws = vmw_winsys_screen(sws); |
| const int slot = tf->slot; |
| uint32_t expected = gettid(); |
| |
| mksstat_tls_global = tf->enclosing; |
| |
| if (slot < 0) |
| return; |
| |
| if (__atomic_compare_exchange_n(&vws->mksstat_tls[slot].pid, &expected, -1U, false, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) { |
| const uint64_t dt = rdtsc() - tf->startTime; |
| MKSGuestStatCounterTime *const counterTime = tf->counterTime; |
| |
| assert(vws->mksstat_tls[slot].stat_pages); |
| assert(vws->mksstat_tls[slot].stat_id != -1UL); |
| |
| __atomic_fetch_add(&counterTime->counter.count.value, 1, __ATOMIC_ACQ_REL); |
| __atomic_fetch_add(&counterTime->selfCycles.value, dt, __ATOMIC_ACQ_REL); |
| __atomic_fetch_add(&counterTime->totalCycles.value, dt, __ATOMIC_ACQ_REL); |
| |
| if (tf->enclosing) { |
| MKSGuestStatCounterTime *const counterTime = tf->enclosing->counterTime; |
| |
| assert(counterTime); |
| |
| __atomic_fetch_sub(&counterTime->selfCycles.value, dt, __ATOMIC_ACQ_REL); |
| } |
| |
| __atomic_store_n(&vws->mksstat_tls[slot].pid, expected, __ATOMIC_RELEASE); |
| } |
| } |
| |
| #endif /* VMX86_STATS */ |
| static void |
| vmw_svga_winsys_stats_inc_noop(struct svga_winsys_screen *sws, |
| enum svga_stats_count index) |
| { |
| /* noop */ |
| } |
| |
| static void |
| vmw_svga_winsys_stats_time_push_noop(struct svga_winsys_screen *sws, |
| enum svga_stats_time index, |
| struct svga_winsys_stats_timeframe *tf) |
| { |
| /* noop */ |
| } |
| |
| static void |
| vmw_svga_winsys_stats_time_pop_noop(struct svga_winsys_screen *sws) |
| { |
| /* noop */ |
| } |
| |
| boolean |
| vmw_winsys_screen_init_svga(struct vmw_winsys_screen *vws) |
| { |
| vws->base.destroy = vmw_svga_winsys_destroy; |
| vws->base.get_hw_version = vmw_svga_winsys_get_hw_version; |
| vws->base.get_cap = vmw_svga_winsys_get_cap; |
| vws->base.context_create = vmw_svga_winsys_context_create; |
| vws->base.surface_create = vmw_svga_winsys_surface_create; |
| vws->base.surface_is_flushed = vmw_svga_winsys_surface_is_flushed; |
| vws->base.surface_reference = vmw_svga_winsys_surface_ref; |
| vws->base.surface_can_create = vmw_svga_winsys_surface_can_create; |
| vws->base.buffer_create = vmw_svga_winsys_buffer_create; |
| vws->base.buffer_map = vmw_svga_winsys_buffer_map; |
| vws->base.buffer_unmap = vmw_svga_winsys_buffer_unmap; |
| vws->base.buffer_destroy = vmw_svga_winsys_buffer_destroy; |
| vws->base.surface_init = vmw_svga_winsys_surface_init; |
| vws->base.fence_reference = vmw_svga_winsys_fence_reference; |
| vws->base.fence_signalled = vmw_svga_winsys_fence_signalled; |
| vws->base.shader_create = vmw_svga_winsys_shader_create; |
| vws->base.shader_destroy = vmw_svga_winsys_shader_destroy; |
| vws->base.fence_finish = vmw_svga_winsys_fence_finish; |
| vws->base.fence_get_fd = vmw_svga_winsys_fence_get_fd; |
| vws->base.fence_create_fd = vmw_svga_winsys_fence_create_fd; |
| vws->base.fence_server_sync = vmw_svga_winsys_fence_server_sync; |
| |
| vws->base.query_create = vmw_svga_winsys_query_create; |
| vws->base.query_init = vmw_svga_winsys_query_init; |
| vws->base.query_destroy = vmw_svga_winsys_query_destroy; |
| vws->base.query_get_result = vmw_svga_winsys_query_get_result; |
| |
| #ifdef VMX86_STATS |
| if (vws->ioctl.have_drm_2_19) { |
| vws->base.stats_inc = vmw_svga_winsys_stats_inc; |
| vws->base.stats_time_push = vmw_svga_winsys_stats_time_push; |
| vws->base.stats_time_pop = vmw_svga_winsys_stats_time_pop; |
| } else { |
| vws->base.stats_inc = vmw_svga_winsys_stats_inc_noop; |
| vws->base.stats_time_push = vmw_svga_winsys_stats_time_push_noop; |
| vws->base.stats_time_pop = vmw_svga_winsys_stats_time_pop_noop; |
| } |
| |
| #else |
| vws->base.stats_inc = vmw_svga_winsys_stats_inc_noop; |
| vws->base.stats_time_push = vmw_svga_winsys_stats_time_push_noop; |
| vws->base.stats_time_pop = vmw_svga_winsys_stats_time_pop_noop; |
| |
| #endif |
| vws->base.host_log = vmw_svga_winsys_host_log; |
| |
| return TRUE; |
| } |
| |
| |